一步步編寫avalon組件01:彈出層組件

avalon2已經穩定下來,是時候教你們如何使用組件這個高級功能了。javascript

組件是咱們實現疊積木開發的關鍵。css

avalon2實現一個組件很是輕鬆,而且如何操做這個組件也比之前的avalon2,仍是react, angular輕鬆多了,不須要flux這樣奇怪的額外設施。html

avalon2的組件包含三部分,以經典的行爲結構樣式相分離。一般咱們命名爲index.js, template.html, style.scss。前端

好比咱們開發一個彈出層組件(有遮罩的那種),其目錄結構就是以下。vue

modal
----|index.js
----|template.html
----|style.scss

clipboard.png

webpack及各類loader配置

index.js是使用nodejs的模塊機制,不過經過webpack,咱們能夠對require進來的文件進行預處理。所以咱們要事先搞定webpack及其一些經常使用loader。其實也很少,就是css-loader, text-loader, style-loader, sass-loader。而sass-loader要依賴node-sass這個巨可怕的模塊,它要依賴更多東西,這對你的網速與耐性有必定要求。爲了讓你儘快搞定這些依賴,建議你設置一下npm 的代理,如使用cnpm。java

npm配置鏡像、設置代理node

前端之Sass/Scss實戰筆記react

安裝webpack時最好指定版本,使用1.13.1,2.*並不穩定。咱們整個應用都使用webpack打包。webpack

咱們創建一個ms-modal的文件夾。而後在命令行底下,使用npm init命令來初始化倉庫吧,一路回車,最後敲上你的大名:git

clipboard.png

而後修改package.json文件,添加devDependencies這個鍵值對,最後變成這樣

{
  "name": "ms-modal",
  "version": "1.0.0",
  "description": "modal",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/RubyLouvre/ms-modal.git"
  },
  "dependencies": {
    "avalon2": "~2.1.1",
    "url-loader": "0.5.7",
    "node-sass": "^3.8.0",
    "sass-loader": "^3.2.2",
    "style-loader": "~0.13.1",
    "css-loader": "~0.8.0",
    "text-loader": "0.0.1",
    "webpack": "^1.13.1"
  },
  "author": "RubyLouvre",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/RubyLouvre/ms-modal/issues"
  },
  "homepage": "https://github.com/RubyLouvre/ms-modal#readme"
}

各個模塊的做用

  1. avalon2 這是核心庫,負責全部DOM操做與數據處理
  2. url-loader 用於抽取圖片與字體等各類資源
  3. sass-loader, node-sass, 用於編譯SCSS文件
  4. css-loader 處理CSS文件中url,添加局做用域支持(css modules)及壓縮或美化CSS
  5. style-loader 建立一個style標籤,將全部模塊引用的樣式所有塞進去,大大減小請求
  6. text-loader 將咱們的模板文件變成一個字符串。羣裏有人建議使用 raw!html-minify 這兩個loader,下次試試
  7. webpack 將工程的全部資源經過各類loader整合起來!

而後npm install

樣式與模板

爲了美觀,咱們用到了iconfont,這個我已經爲大家準備好了。

創建一個子目錄放 iconfont字體。

而後創建一個font.scss,內容省去,不是咱們學習的重點。

創建一個btn.scss子模塊,內容省去,不是咱們學習的重點。

再創建一個style.scss模塊,它引入btn.scss, font.scss,內容以下:

@import "./font.scss";
@import "./btn.scss";
.modal-mask{
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(55,55,55,.6);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;

}

.confirm-content{
  padding-left: 30px;
  padding-top: 30px;
  padding-bottom: 30px;
}



.modal-confirm{
  width: 400px;
  box-sizing: border-box;
  padding: 30px 40px;
  background-color: #fff;
  border-radius: 6px;
  transition: transform .3s ease;
  i{
    color: #fa0;
    font-size: 24px;
    position: relative;
    top: 2px;
  }

  .confirm-btns{
    text-align: right;
  }
}

.modal-box{
  width: 520px;
  box-sizing: border-box;
  background-color: #fff;
  border-radius: 6px;
}


@media only screen and (max-width: 640px) {

  .modal-confirm{
    width: 100%;
    margin: 0 20px;
    padding: 10px 20px;

  }
  .modal-box{
    width: 100%;
    margin: 0 20px;
  }

}

.modal-header{
  padding: 13px 18px 14px 16px;
  border-bottom: 1px solid #e9e9e9;
  position: relative;
  i{
    position: absolute;
    right: 20px;
    top: 15px;
    font-size: 14px;
    cursor: pointer;
  }
  h3{
    font-size: 14px;
  }
}

.modal-body{
  padding: 16px;
}

.modal-footer{
  padding: 10px 18px 10px 10px;
  border-top: 1px solid #e9e9e9;
  background: #fff;
  border-radius: 0 0 6px 6px;
  text-align: right;
}

.modal-enter {
  opacity: 0;
}
.modal-enter-active{
   opacity: 1;
   .modal-confirm{
      transform: scale(1.1); 
   }
   .modal-box{
      transform: scale(1.1); 
   }
}
.modal-leave {
   opacity: 1;
  
}
.modal-leave-active{
   opacity: 0;
   .modal-confirm{
      transform: scale(1.1); 
   }
   .modal-box{
      transform: scale(1.1); 
   }
}
.modal-enter,.modal-leave {
  transition: all .3s ease;
}

注意: scss文件中不能出現 中文,不然會編譯失敗

這裏用到了動畫效果,使用modal-enter, modal-leave, modal-enter-active, modal-leave-active等類名實現,原理與angular是一致的。能夠到這裏溫習一下。

再創建一個template.html.之因此不學vue, polymer將組件全部東西併成一個文件,是由於對於普通的html文件,全部IDE或文本編輯器都有自帶的語法高亮。而且裏面須要什麼處理,能夠直接在webpack中配置。

template.html的內容以下:

<div class="modal-mask"  ms-visible="@isShow" ms-effect="{is:'modal'}">
    <div class="modal-box">
        <div class="modal-header">
            <h3>{{@title}}</h3>
            <i class="iconfont icon-cross" ms-click="@cbProxy(false)"></i>
        </div>
        <div class="modal-body">
            <slot name="content"></slot>
        </div>
        <div class="modal-footer">
            <button class="btn" ms-click="@cbProxy(false)">取 消</button>
            <button class="btn btn-primary" ms-click="@cbProxy(true)">確 定</button>
        </div>
    </div>
</div>

須要說明一下modal組件通常要直接放在body底下,是其直接子元素,方便其蒙板能罩住整個頁面。上面的.modal-mask就是蒙板,.model-box就是彈出層,彈出層裏面又分三大部分,標題欄,內容區與底部的按鈕區。內容區好比複雜,咱們使用DOM插入點機制來設置,換言之,那裏使用slot元素佔位。之後咱們直接在自定義標籤裏面添加對應標籤,它就會挪到slot的位置上了!

視圖模型

modal組件通常有以下屬性:

  1. isShow: 用於控制顯示與否
  2. title: 標題
  3. content: 內容,這個是一個很是複雜的HTML結構
  4. 回調: 這裏設計了兩種回調onOk, onCancel。它們的行爲很相近,所以我在模板上都封裝成cbProxy,經過傳參來區分它們。

而後是組件自己index.js

到目前爲止,咱們的目錄以下:

clipboard.png

index.js主要是引用模板與樣式與avalon2主庫,而後代碼主體爲avalon.component這個方法的使用。

var avalon = require('avalon2')
require('./style.scss')

avalon.component('ms-modal', {
    template: require('text!./template.html'),
    defaults: {
        title:'modal',
        isShow: true,
        cbProxy: function(ok){
           
        }
    },
    soleSlot: 'content'
})

defaults對象裏面定義組件要用到的屬性,soleSlot是佔位元素slot的名字。

事件與鉤子

關鍵是dbProxy的實現,咱們要求點擊上面的叉叉與下面的取消按鈕時調用onCanel回調,若是這個回調什麼都不返回,就直接隱藏彈層。若是onCancel是返回false或是返回一個相似Promise的對象(帶next方法),就不會隱藏彈層。 點擊其確認按鈕,則是調用onOk回調,其餘邏輯與onCancel類似!

cbProxy: function (ok) {
    var cbName = ok ? 'onConfirm' : 'onClose'
    if (this.hasOwnProperty(cbName)) {
        var ret = this[cbName]()
        if (ret !== false || (ret && typeof ret.next === 'function')) {
            this.isShow = false
        }
    } else {
        this.isShow = false
    }
}

而後我在組件onReady鉤子處理一些樣式問題,好比說你要蓋住整個窗口,若是不想拉大整個蒙板,那麼最好就是將body的overflow:hidden。 此外還要阻止第一次動畫效果。

onReady: function(){
            var el = this.$element
            el.style.display = 'none'//強制阻止動畫發生
            
            this.$watch('isShow', function(a){
                if(a){
                   document.body.style.overflow = 'hidden' 
                }else{
                   document.body.style.overflow = ''
                }
            })
        }

到此爲止,咱們的組件已經寫好了,是否是快得不可思議。

而後咱們創建一個main.js入口文件,它裏面調用modal組件或引入其餘業務代碼,爲簡單起見,咱們如今只是定義了一個頁面vm。

var avalon = require('avalon2')
require('./index')
avalon.define({
    $id: 'test',
    show: function(){
        this.config.isShow = true
    },
    config: {
        isShow: false,
        onCancel: function(){
            alert('cancel')
        },
        onOk: function(){
            alert('ok')
        },
        title:'這是測試'
    }
})

module.exports = avalon

注意,最後必須返回avalon,用於webpack.cofig的 output配置
固然之後改一下webpack.config,可能就不須要這樣寫。有關如何利用webpack 進行工程化,我也在不斷探索。你們有好的方案能夠留言給我。

打包與運行

咱們再看一下如何打包。建議一個webpack.config.js,內容以下:

var webpack = require('webpack');

var path = require('path');


function heredoc(fn) {
    return fn.toString().replace(/^[^\/]+\/\*!?\s?/, '').
            replace(/\*\/[^\/]+$/, '').trim().replace(/>\s*</g, '><')
}
var api = heredoc(function () {
    /*
     avalon的彈出層組件
     1.  isShow: 用於控制顯示與否
     2.  title: 標題
     3.  content: 內容,這個是一個很是複雜的HTML結構
     4.  onOk
     5:  onCancel
     
     使用
     兼容IE6-8
    
     <xmp ms-widget="[{is:'ms-modal'}, @config]">
     <p>彈窗的內容</p>
     <p>彈窗的內容</p>
     <p>彈窗的內容結束!</p>
     </xmp>
  
     只支持現代瀏覽器(IE9+)
    
     <ms-modal ms-widget="@config">
     <p>彈窗的內容</p>
     <p>彈窗的內容</p>
     <p>彈窗的內容結束!</p>
     </ms-modal>
      
     */
})

module.exports = {
    entry: {
        index: './main'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js',
        libraryTarget: 'umd',
        library: 'avalon'
    }, //頁面引用的文件
    plugins: [
        new webpack.BannerPlugin('彈出層組件 by 司徒正美\n' + api)
    ],
    module: {
        loaders: [
            //ExtractTextPlugin.extract('style-loader', 'css-loader','sass-loader')
            //http://react-china.org/t/webpack-extracttextplugin-autoprefixer/1922/4
            //http://stackoverflow.com/questions/34639720/webpack-font-include-issue
            // https://github.com/b82/webpack-basic-starter/blob/master/webpack.config.js 
            {test: /\.scss$/, loader:'style!css!sass',exclude: /node_modules/},
            {test: /\.(ttf|eot|svg|woff2?)((\?|#)[^\'\"]+)?$/, loader: 'url-loader'}

        ]
    },
    
    resolve: {
        extensions: ['.js', '', '.css']
    }
}

咱們執行webpack命令,它就合併成一個文件,放在dist目錄下,但有時你死活裝不上node-sass什麼,搞不定SASS的編譯,也沒問題,能夠koala編譯好,直接在index.js中引用純CSS文件。

最後創建一個page.html文件,欣賞一下咱們的勞動成果:

<!DOCTYPE html>
<html>
    <head>
        <title>modal</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="./dist/index.js"></script>
    </head>
    <body ms-controller="test">
        <xmp ms-widget="[{is:'ms-modal'}, @config]">
          <p>彈窗的內容</p>
          <p>彈窗的內容</p>
          <p>彈窗的內容結束!</p>
        </xmp>
    <p><button ms-click="@show">顯示彈出</button></p>
    </body>
</html>

你們能夠到這裏下載到此工程

圖片描述

其餘參考資料:

  1. 如何將網頁CSS背景圖高斯模糊且全屏顯示
相關文章
相關標籤/搜索