Web Components 入門(mén)實(shí)戰(zhàn)學(xué)習(xí)

Web Components 入門(mén)實(shí)戰(zhàn)學(xué)習(xí).png

前言:這周完成了兩場(chǎng)技術(shù)分享會(huì)霎褐,下周還有一場(chǎng)眼虱,就完成了這階段的一個(gè)重大任務(wù)勒虾。分享會(huì)是關(guān)于 TS 的停撞,我這兩場(chǎng)分享會(huì)的主題分別是:

  • TS 初級(jí)入門(mén)
  • TS 高級(jí)語(yǔ)法

下周的主題是县踢,如何在 React 中優(yōu)雅的書(shū)寫(xiě) TS转绷。

我對(duì)技術(shù)也是有那么點(diǎn)喜歡,所以我平時(shí)喜歡學(xué)習(xí)些新技術(shù)硼啤,但是同時(shí)我認(rèn)為最好的學(xué)習(xí)议经,應(yīng)該是來(lái)自于實(shí)踐,所以除了大量做項(xiàng)目谴返,對(duì)剛學(xué)的技術(shù)最好的幫助就是分享煞肾,把別人給教會(huì),而這也是一種能力的體現(xiàn)嗓袱。所以我對(duì)分享并不是很排斥籍救,反而有種強(qiáng)烈的喜歡。

而且分享還能打破封閉索抓,對(duì)個(gè)人能力有很大的加成作用钧忽,不過(guò)難就難在跨出第一步毯炮,我剛開(kāi)始分享也是有點(diǎn)慌逼肯,但等到第二場(chǎng)就開(kāi)始駕輕就熟了,真的鼓勵(lì)大家要不斷的去嘗試桃煎,不要重復(fù)自己篮幢,要敢于突破自己。

Web Components 這個(gè)技術(shù)是我在 「TS 高級(jí)語(yǔ)法」主題分享前給團(tuán)隊(duì)小伙伴的一個(gè)開(kāi)胃小菜为迈。


以下正文:

前端組件化

無(wú)論你用什么流行框架去寫(xiě)前端三椿,本質(zhì)上你都是在使用前端三劍客即: HTML、CSS 和 JavaScript葫辐。那這三劍客在自己的領(lǐng)域組件化/模塊化做的怎么樣了呢搜锰?

  • 對(duì)于 CSS,我們有 @impot耿战。
  • 對(duì)于 JS 現(xiàn)在也有模塊化方案蛋叼。

那么對(duì)于 HTML 呢?我們知道樣式和腳本都是集成到 HTML 中剂陡,所以所以單獨(dú)的去做 HTML 模塊化狈涮,沒(méi)有任何意義。

既然如此鸭栖,我們看看 HTML 在編程過(guò)程中遇到了什么問(wèn)題歌馍。

  1. 因?yàn)?CSS 樣式作用在全局,就會(huì)造成樣式覆蓋晕鹊。
  2. 因?yàn)樵陧?yè)面中只有一個(gè) DOM松却,任何地方都可以直接讀取和修改 DOM暴浦。

可以看到我們的痛點(diǎn)就是解決 CSS 和 DOM 這兩個(gè)阻礙組件化的因素,于是 Web Components 孕育而生玻褪。

Web Components

Web Components 由三項(xiàng)主要技術(shù)組成:

Web Components 整體知識(shí)點(diǎn)不多同规,內(nèi)容也不復(fù)雜,我認(rèn)為核心就是 Shadow DOM(影子 DOM)窟社,為什么我這么認(rèn)為呢券勺?看下 Shadow DOM 的作用你就明白了:

  • 影子 DOM 中的元素對(duì)于整個(gè)網(wǎng)頁(yè)是不可見(jiàn)的;
  • 影子 DOM 的 CSS 不會(huì)影響到整個(gè)網(wǎng)頁(yè)的 CSSOM灿里,影子 DOM 內(nèi)部的 CSS 只對(duì)內(nèi)部的元素起作用关炼。

看完,你發(fā)沒(méi)發(fā)現(xiàn)它剛好解決了匣吊,我們開(kāi)頭前端組件遇到的問(wèn)題儒拂,所以 Shadow DOM 才是 Web Components 的核心。

自定義元素(Custom elements)

如何自定義元素或叫如何自定義標(biāo)簽

自定義元素就像 Vue 和 React 中的類(lèi)組件色鸳,首先我們需要使用 ES2015 語(yǔ)法來(lái)定義一個(gè)類(lèi)社痛,接著,使用瀏覽器原生的 customElements.define() 方法命雀,告訴瀏覽器我要注冊(cè)一個(gè)元素/標(biāo)簽 user-text蒜哀,(自定義元素的名稱(chēng)必須包含連詞線,用與區(qū)別原生的 HTML 元素吏砂,就像 React 的自定義組件名使用時(shí)必須大寫(xiě)一樣)撵儿。

class UserText extends HTMLElement {
    constructor() {
        super();
    }
}

上面代碼中,UserText 是自定義元素的類(lèi)狐血,這個(gè)類(lèi)繼承了 HTMLElement 父類(lèi)淀歇。

我們現(xiàn)在把 user-text 作為標(biāo)簽使用,放到頁(yè)面上去:

<!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>
    <user-text></user-text>
    <script>
        class UserText extends HTMLElement {
            constructor () {
                super();
                this.innerHTML = "我是內(nèi)容";
            }
        }
        globalThis.customElements.define("user-text", UserText);
    </script>
</body>
</html>

我們看到頁(yè)面成功渲染:

user-text.png

組件會(huì)有生命周期匈织,所以這個(gè)類(lèi)還有些方法:

  • connectedCallback:當(dāng) custom element 首次被插入文檔 DOM 時(shí)浪默,被調(diào)用,俗稱(chēng)組件上樹(shù)报亩。
  • disconnectedCallback:當(dāng) custom element 從文檔 DOM 中刪除時(shí)浴鸿,被調(diào)用,俗稱(chēng)組件下樹(shù)或組件消亡弦追。
  • adoptedCallback:當(dāng) custom element 被移動(dòng)到新的文檔時(shí)岳链,被調(diào)用,這個(gè) API 常和 document.adoptNode 配合使用约急。
  • attributeChangedCallback: 當(dāng) custom element 增加、刪除苗分、修改自身屬性時(shí)厌蔽,被調(diào)用,俗稱(chēng)組件更新摔癣。

模板 (Templates)

頁(yè)面上的元素最終是要給用戶(hù)呈現(xiàn)內(nèi)容奴饮,在自定義組件里,我們通過(guò)字符串的方式來(lái)接受要展現(xiàn)給用戶(hù)的內(nèi)容择浊,這種方式非常不利于組織我們的 HTML戴卜,我們需要一個(gè)寫(xiě) HTML 的地方,這個(gè)技術(shù)就是模板 (Templates)琢岩,非常像 Vue 的模版渲染投剥,如果你熟悉 Vue ,完全可以無(wú)障礙切換担孔。

我們隨便來(lái)弄點(diǎn)數(shù)據(jù)組織下代碼江锨,在瀏覽器展示給用戶(hù):


<!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>
    <template id="user-text-template">
        你好,我是模版糕篇!
    </template>
    <user-text></user-text>
    <script>
        class UserText extends HTMLElement {
            constructor () {
                super();
            }
            connectedCallback () {
                const oldNode = document.getElementById("user-text-template").content;
                const newNode = oldNode.cloneNode(true);
                this.appendChild(newNode);
            }
        }
        globalThis.customElements.define("user-text", UserText);
    </script>
</body>
</html>

我們看到頁(yè)面成功渲染:

template-render.png

如果啄育,自定義元素需要?jiǎng)討B(tài)傳值給我們的自定義組件,可以使用插槽 slot娩缰,語(yǔ)法基本同 Vue灸撰,但是此時(shí)還無(wú)法演示谒府,因?yàn)?slot 標(biāo)簽對(duì)標(biāo)準(zhǔn)的 DOM(更專(zhuān)業(yè)點(diǎn)叫 light DOM)無(wú)效拼坎,只對(duì) shadow DOM 是有效的,看下使用示例完疫。

<!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>
    <template id="user-text-template">
        <style>
            p {
                color: red;
            }
        </style>
        <p id="templateDOM">你好泰鸡,我是模版!</p>
        <p><slot>因?yàn)槲沂菬o(wú)效的壳鹤,我也會(huì)默認(rèn)展示</slot></p>
    </template>
    <user-text>
        <p>light DOM 環(huán)境下盛龄,slot 標(biāo)簽沒(méi)用</p>
    </user-text>
    <script>
        class UserText extends HTMLElement {
            constructor () {
                super();
            }
            connectedCallback () {
                const oldNode = document.getElementById("user-text-template").content;
                const newNode = oldNode.cloneNode(true);
                this.appendChild(newNode);
            }
        }
        globalThis.customElements.define("user-text", UserText);
        console.log(document.getElementById("templateDOM"));
    </script>
</body>
</html>

看下頁(yè)面加載顯示:

slot-invaild-light-dom.png

除了,slot 無(wú)法使用芳誓,我們還觀察到 template 元素及其內(nèi)容不會(huì)在 DOM 中呈現(xiàn)余舶,必須通過(guò) JS 的方式去訪問(wèn)、style 標(biāo)簽內(nèi)的樣式是作用到全局的锹淌、template 里面的 DOM 也可以被全局訪問(wèn)匿值。

影子 DOM(shadow DOM)

影子 DOM 是 Web Components 核心中的核心,可以一舉解決我們前面提到的赂摆,CSS 和 DOM 作用全局的問(wèn)題挟憔。

看下使用示例:

<!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>
    <template id="user-text-template">
        <style>
            p {
                color: red;
            }
        </style>
        <p id="templateDOM">你好钟些,我是模版!</p>
        <p><slot>因?yàn)槲沂菬o(wú)效的绊谭,我也會(huì)默認(rèn)展示</slot></p>
    </template>
    <user-text>
        <p>light DOM 環(huán)境下政恍,slot 標(biāo)簽沒(méi)用</p>
    </user-text>
    <p>測(cè)試 shadow DOM 樣式不作用全局</p>
    <script>
        class UserText extends HTMLElement {
            constructor () {
                super();
            }
            connectedCallback () {
                this.attachShadow({ mode: "open" });
                const oldNode = document.getElementById("user-text-template").content;
                const newNode = oldNode.cloneNode(true);
                this.shadowRoot.appendChild(newNode);
            }
        }
        globalThis.customElements.define("user-text", UserText);
        console.log(document.getElementById("templateDOM"));
    </script>
</body>
</html>

現(xiàn)在完成了,組件的樣式應(yīng)該與代碼封裝在一起达传,只對(duì)自定義元素生效篙耗,不影響外部的全局樣式、DOM 默認(rèn)與外部 DOM 隔離宪赶,內(nèi)部任何代碼都無(wú)法影響外部鹤树,同時(shí) slot 也生效了,看下頁(yè)面加載顯示:

obstacle-style-dom.png

影子 DOM 的 mode 參數(shù)除了有 open逊朽,之外還有 closed罕伯,兩者的區(qū)別在于此影子 DOM 是否能被訪問(wèn)外界訪問(wèn),即是否能通過(guò) JS 獲取影子 DOM 讀取 影子 DOM 里面的內(nèi)容叽讳。

style 穿越 影子 DOM

任何項(xiàng)目為了統(tǒng)一風(fēng)格追他,肯定需要有公共樣式,而且為了方面是統(tǒng)一引入的岛蚤,這就涉及到外部樣式影響到內(nèi)部樣式邑狸,那怎么突破影子 DOM 呢?

CSS 變量

可以使用 CSS 變量來(lái)穿透 DOM:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS 變量樣式穿透</title>
    <style>
        [type="primary"] {
            --ui-button-border: 1px solid transparent;
            --ui-button-background: deepskyblue;
            --ui-button-color: #fff;
        }
    </style>
</head>
<body>
    <template id="ui-button-template">
        <style>
            button {
                cursor: pointer;
                padding: 9px 1em;
                border: var(--ui-button-border, 1px solid #ccc);
                border-radius: var(--ui-button-radius, 4px);
                background-color: var(--ui-button-background, #fff);
                color:  var(--ui-button-color, #333);
            }
        </style>
        <button ><slot></slot></button>
    </template>
    <ui-button type="primary">按鈕</ui-button>
    <script>
        class UiButton extends HTMLElement {
            constructor () {
                super();
            }
            connectedCallback () {
                this.attachShadow( { mode: "open" });
                const oldNode = document.getElementById("ui-button-template").content;
                const newNode = oldNode.cloneNode(true);
                this.shadowRoot.appendChild(newNode);
            }
        }
        globalThis.customElements.define("ui-button", UiButton);
    </script>
</body>
</html>

頁(yè)面展示效果圖:

::part 偽元素

::part 偽元素的用法有點(diǎn)像具名插槽 slot涤妒。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>::part 樣式穿透</title>
    <style>
        [type="primary"]::part(button) {
            cursor: pointer;
                padding: 9px 1em;
                border: 1px solid #ccc;
                border-radius: 4px;
                background-color: skyblue;;
                color:  #987;
        }
    </style>
</head>
<body>
    <template id="ui-button-template">
        <button part="button"><slot></slot></button>
    </template>
    <ui-button type="primary">按鈕</ui-button>
    <script>
        class UiButton extends HTMLElement {
            constructor () {
                super();
            }
            connectedCallback () {
                this.attachShadow( { mode: "open" });
                const oldNode = document.getElementById("ui-button-template").content;
                const newNode = oldNode.cloneNode(true);
                this.shadowRoot.appendChild(newNode);
            }
        }
        globalThis.customElements.define("ui-button", UiButton);
    </script>
</body>
</html>

HTML 原生組件支持 Web Components

我們知道 HTML5 有很多的原生組件单雾,例如:input,video她紫,textarea硅堆,select,audio 等贿讹。

如果你審查元素會(huì)發(fā)現(xiàn)渐逃,這個(gè)組件并不是純正的原生組件,而是基于 Web Components 來(lái)封裝的民褂。

如果你審查元素沒(méi)有顯示影子 DOM茄菊,請(qǐng)打開(kāi)控制臺(tái),同時(shí)檢查瀏覽器設(shè)置 Settings -> Preferences -> Elements 中把 Show user agent shadow DOM 打上勾赊堪。

落地應(yīng)用有哪些面殖?

首先,github 網(wǎng)址是完全基于 Web Components 來(lái)開(kāi)發(fā)的哭廉,其次 Vue 和 小程序 也是基于 Web Components 來(lái)做組件化的脊僚,而且 Web Components 作為最底層的技術(shù)完全可配合 Vue 和 React 等框架,直接使用的群叶。

光學(xué)不練那不是假把式嗎吃挑,我來(lái)給大家整個(gè) demo钝荡,自定義一個(gè)對(duì)話框,這個(gè)對(duì)話框只滿(mǎn)足最基本的使用需求舶衬,先看下最終的成品埠通。

對(duì)話框

源代碼,可能比較難得兩個(gè)思路:

  • 數(shù)據(jù)更新逛犹,采用的是類(lèi)的 get 和 set
  • 關(guān)閉的回調(diào)事件端辱,用的是自定義事件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>自定義彈框</title>
</head>
<body>
    <style>
        .open-button {
            cursor: pointer;
            padding: 9px 1em;
            border: 1px solid transparent;
            border-radius: 4px;
            background-color: deepskyblue;
            color: #fff;
        }
        ul > li {
            margin: 20px;
        }
    </style>

    <section>
        <ul>
            <li><button id="launch-dialog-one" class="open-button">open-one</button>
            <li><button id="launch-dialog-two" class="open-button">open-two</button></li>
            <li><button id="launch-dialog-three" class="open-button">open-three</button></li></li>
        </ul>
    </section>

    <shanshu-dialog title="title-one" id="shanshu-dialog-one">
        <span slot="my-text">Let's have some different text!</span>
        <p>Some contents Some contents......</p>
        <p>Some contents Some contents......</p>
        <p>Some contents Some contents......</p>
    </shanshu-dialog>
    
    <shanshu-dialog title="title-two" id="shanshu-dialog-two">
        <span slot="my-text">Let's have some different text!</span>
        <p>Some contents Some contents......</p>
        <p>Some contents Some contents......</p>
        <p>Some contents Some contents......</p>
    </shanshu-dialog>
    
    <shanshu-dialog title="title-three" id="shanshu-dialog-three">
        <span slot="my-text">Let's have some different text!</span>
        <p>Some contents Some contents......</p>
        <p>Some contents Some contents......</p>
        <p>Some contents Some contents......</p>
    </shanshu-dialog>

    <template id="shanshu-dialog-template">
        <style>
            .wrapper {
                opacity: 0;
                transition: visibility 0s, opacity 0.25s ease-in;
            }

            .wrapper:not(.open) {
                visibility: hidden;
            }

            .wrapper.open {
                align-items: center;
                display: flex;
                justify-content: center;
                height: 100vh;
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                opacity: 1;
                visibility: visible;
            }

            .overlay {
                background: rgba(0, 0, 0, 0.3);
                height: 100%;
                position: fixed;
                top: 0;
                right: 0;
                bottom: 0;
                left: 0;
                width: 100%;
            }

            .dialog {
                background: #ffffff;
                max-width: 600px;
                min-width: 400px;
                text-align: center;
                padding: 1rem;
                position: fixed;
                border-radius: 4px;
            }

            button {
                all: unset;
                cursor: pointer;
                font-size: 1.25rem;
                position: absolute;
                top: 1rem;
                right: 1rem;
            }

            button:focus {
                border: 1px solid skyblue;
            }
            h1 {
                color: #4c5161;
            }
            .content {
                color: #34495e;
                position: relative;
            }
            .btn {
                background: none;
                outline: 0;
                border: 0;
                position: absolute;
                right: 1em;
                top: 1em;
                width: 20px;
                height: 20px;
                padding: 0;
                user-select: none;
                cursor: unset;
            }
            .btn::before {
                content: "";
                display: block;
                border: 1px solid green;
                height: 20px;
                width: 0;
                border-radius: 2px;
                /*transition: .1s;*/
                transform: translate(9px) rotate(45deg);
                background: #fff;
            }
            .btn::after {
                content: "";
                display: block;
                border: 1px solid green;
                height: 20px;
                border-radius: 2px;
                width: 0;
                /*transition: .1s;*/
                transform: translate(9px, -100%) rotate(-45deg);
                background: #fff;
            }
        </style>
        <div class="wrapper">
            <div class="overlay"></div>
            <div class="dialog" role="dialog" aria-labelledby="title" aria-describedby="content">
                <button aria-label="Close" class="btn"></button>
                <h1 id="title">Hello world</h1>
                <div id="content" class="content">
                    <slot></slot>
                    <slot name="my-text"></slot>
                </div>
            </div>
        </div>
    </template>
    
    <script type="text/javascript">
        "use strict";
        class ShanshuDialog extends HTMLElement {
            static get observedAttributes() {
                return ["open"];
            }
            constructor() {
                super();
                this.attachShadow({ mode: "open" });
                this.close = this.close.bind(this);
            }
            connectedCallback() {
                const { shadowRoot } = this;
                const templateElem = document.getElementById("shanshu-dialog-template");
                const oldNode = templateElem.content;
                // const newNode = oldNode.cloneNode(true);
                const newNode = document.importNode(oldNode, true);
                shadowRoot.appendChild(newNode);
                shadowRoot.getElementById("title").innerHTML = this.title;
                shadowRoot.querySelector("button").addEventListener("click", this.close);
                shadowRoot.querySelector(".overlay").addEventListener("click", this.close);
            }
            disconnectedCallback() {
                this.shadowRoot.querySelector("button").removeEventListener("click", this.close);
                this.shadowRoot.querySelector(".overlay").removeEventListener("click", this.close);
            }
            get open() {
                return this.hasAttribute("open");
            }
            set open(isOpen) {
                console.log("isOpen", isOpen);
                const { shadowRoot } = this;
                shadowRoot.querySelector(".wrapper").classList.toggle("open", isOpen);
                shadowRoot.querySelector(".wrapper").setAttribute("aria-hidden", !isOpen);
                if (isOpen) {
                    this._wasFocused = document.activeElement;
                    this.setAttribute("open", false);
                    this.focus();
                    shadowRoot.querySelector("button").focus();
                } else {
                    this._wasFocused && this._wasFocused.focus && this._wasFocused.focus();
                    this.removeAttribute("open");
                    this.close();
                }
            }
            close() {
                this.open !== false && (this.open = false);
                const closeEvent = new CustomEvent("dialog-closed");
                this.dispatchEvent(closeEvent);
            }
        }
        customElements.define("shanshu-dialog", ShanshuDialog);


        const buttonOneDOM = document.getElementById("launch-dialog-one");
        const buttonTwoDOM = document.getElementById("launch-dialog-two");
        const buttonThreeDOM = document.getElementById("launch-dialog-three");
        const shanshuDialogOne = document.querySelector("#shanshu-dialog-one");

        buttonOneDOM.addEventListener("click", () => {
            document.querySelector("#shanshu-dialog-one").open = true;
        });
        shanshuDialogOne.addEventListener("dialog-closed", () => {
            alert("對(duì)話框關(guān)閉回調(diào)函數(shù)");
        });

        buttonTwoDOM.addEventListener("click", () => {
            document.querySelector("#shanshu-dialog-two").open = true;
        });
        buttonThreeDOM.addEventListener("click", () => {
            document.querySelector("#shanshu-dialog-three").open = true;
        });
        
    </script>
</body>
</html>

組件庫(kù)

當(dāng)我們談到在項(xiàng)目中如何應(yīng)用,我們首先需要兩個(gè)東西虽画,選個(gè) UI 組件庫(kù)舞蔽,同時(shí)有比較好的工具來(lái)操作這個(gè) UI 庫(kù),我提供兩個(gè)給你參考码撰。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渗柿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子脖岛,更是在濱河造成了極大的恐慌朵栖,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柴梆,死亡現(xiàn)場(chǎng)離奇詭異陨溅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绍在,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)门扇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人偿渡,你說(shuō)我怎么就攤上這事臼寄。” “怎么了卸察?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵脯厨,是天一觀的道長(zhǎng)铅祸。 經(jīng)常有香客問(wèn)我坑质,道長(zhǎng),這世上最難降的妖魔是什么临梗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任涡扼,我火速辦了婚禮,結(jié)果婚禮上盟庞,老公的妹妹穿的比我還像新娘吃沪。我一直安慰自己,他們只是感情好什猖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布票彪。 她就那樣靜靜地躺著红淡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪降铸。 梳的紋絲不亂的頭發(fā)上在旱,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音推掸,去河邊找鬼桶蝎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛谅畅,可吹牛的內(nèi)容都是我干的登渣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼毡泻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼胜茧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起仇味,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤竹揍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后邪铲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體芬位,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年带到,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昧碉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡揽惹,死狀恐怖被饿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搪搏,我是刑警寧澤狭握,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站疯溺,受9級(jí)特大地震影響论颅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜囱嫩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一恃疯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧墨闲,春花似錦今妄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)犬性。三九已至,卻和暖如春腾仅,著一層夾襖步出監(jiān)牢的瞬間仔夺,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工攒砖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缸兔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓吹艇,卻偏偏與公主長(zhǎng)得像惰蜜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子受神,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Web Components抛猖,我們簡(jiǎn)稱(chēng)WC。 Custom Element customElements是瀏覽器內(nèi)...
    brandonxiang閱讀 420評(píng)論 0 1
  • 組件的概念 組件鼻听,是數(shù)據(jù)和方法的一個(gè)封裝财著,其定義了一個(gè)可重用的軟件元素的功能,展示和使用撑碴,通常表現(xiàn)為一個(gè)或一組可重...
    zx_lau閱讀 2,438評(píng)論 0 3
  • 在使用Web Components之前撑教,我們先看看上一篇文章Web Components簡(jiǎn)介[https://ww...
    張中華閱讀 490評(píng)論 0 1
  • 前言 不知不覺(jué),2019年即將接近尾聲醉拓,現(xiàn)有前端三大框架也各自建立著自己的生態(tài)伟姐、自己的使用群體。從angular1...
    Kaku_fe閱讀 2,775評(píng)論 0 19
  • 組件化亿卤,標(biāo)簽語(yǔ)義化愤兵,是前端發(fā)展的趨勢(shì)。現(xiàn)在流行的組件化框架有React排吴、Vue等秆乳,標(biāo)簽語(yǔ)義化在H5中添加的arti...
    張中華閱讀 336評(píng)論 0 1