avalon2已经稳定下来,是时候教大家如何使用组件这个高级功能了。

组件是我们实现叠积木开发的关键。

avalon2实现一个组件非常轻松,并且如何操作这个组件也比以前的avalon2,还是react, angular轻松多了,不需要flux这样奇怪的额外设施。

avalon2的组件包含三部分,以经典的行为结构样式相分离。通常我们命名为index.js, template.html, style.scss。

比如我们开发一个弹出层组件(有遮罩的那种),其目录结构就是如下。

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

webpack及各种loader配置

index.js是使用nodejs的模块机制,不过通过webpack,我们可以对require进来的文件进行预处理。因此我们要事先搞定webpack及其一些常用loader。其实也不多,就是css-loader, text-loader, style-loader, sass-loader。而sass-loader要依赖node-sass这个巨可怕的模块,它要依赖更多东西,这对你的网速与耐性有一定要求。为了让你尽快搞定这些依赖,建议你设置一下npm 的代理,如使用cnpm。

npm配置镜像、设置代理

前端之Sass/Scss实战笔记

安装webpack时最好指定版本,使用1.13.1,2.*并不稳定。我们整个应用都使用webpack打包。

我们建立一个ms-modal的文件夹。然后在命令行底下,使用npm init命令来初始化仓库吧,一路回车,最后敲上你的大名:

然后修改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

到目前为止,我们的目录如下:

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背景图高斯模糊且全屏显示

一步步编写avalon组件01:弹出层组件的更多相关文章

  1. Layui 弹出层组件——layer的模块化开发实例应用

    Layui 弹出层组件——layer的模块化开发实例应用 1.首先在package.json中引入layer组件依赖 2.在源码中应用这个依赖 3.在源码中编写代码应用此组件 4.效果验证:点击日历上 ...

  2. zepto弹出层组件

    html: <!DOCTYPE html> <html> <meta charset="utf-8"> <title></ti ...

  3. 原生Js弹窗插件|web弹出层组件|对话框

    wcPop.js 是一款基于原生javascript开发的前端 web版 弹窗组件,遵循原生 H5/css3/JS 的书写规范,简单实用.拿来即用(压缩后仅10KB).已经兼容各大主流浏览器.内含多种 ...

  4. javascript对话框(弹出层)组件

    http://www.blueidea.com/download/product/2010/7513.asp#comment 1. 从头到尾对一遍<<Javascript高级程序设计> ...

  5. Layui 弹出层组件——layer

    layer是作为Layui[经典模块化前端框架]的一个弹层模块,由于其用户基数较大,所以把layer作为独立组件来维护. 基础参数: 基础参数主要指调用方法时用到的配置项,如:layer.open({ ...

  6. 微信小程序 - 弹出层组件

    需要的可以下载示例:maskalert

  7. Layer组件多个iframe弹出层打开与关闭及参数传递

    一.Layer简介 Layer是一款近年来备受青睐的web弹层组件,基于jquery,易用.实用,兼容包括IE6在内的所有主流浏览器,拥有丰富强大的可自定义的功能. Layer官网地址:http:// ...

  8. html/css/js-layer弹出层的初次使用

    学习前端有时很多时候要用到弹出层,原生的js写有些麻烦,而且不美观,基于jQuery的弹出层组件layer应运而生,近些年来备受青睐.官网上有使用教程,但当初用的时候还是糊里糊涂,今天来记录一下lay ...

  9. 一步步编写avalon组件02:分页组件

    本章节,我们做分页组件,这是一个非常常用的组件.grid, listview都离不开它.因此其各种形态也有. 本章节教授的是一个比较纯正的形态,bootstrap风格的那种分页栏. 我们建立一个ms- ...

随机推荐

  1. 《Qt Quick 4小时入门》学习笔记2

    http://edu.csdn.net/course/detail/1042/14805?auto_start=1   Qt Quick 4小时入门 第五章:Qt Quick基本界面元素介绍   1. ...

  2. 数据结构之图 Part2 - 1

    邻接矩阵 网上很少有C# 写图的数据结构的例子,实际的项目中也从来没用过Array 这坨东西,随手写个,勿喷. namespace LH.GraphConsole { public struct Gr ...

  3. Delphi 使用CHM文件制作系统帮助文档(上下文感知帮助的制作)

    一.基础知识简介         使用帮助提示窗口或状态栏只能提供简单.单一的帮助,无法对某一模块或应用程序整体提供系统的 帮助,因此运行Windows应用程序,需要帮助时一般都可以通过执行帮助菜单获 ...

  4. 通过GCD、NSOperationQueue队列、NSThread三种方法来创建多线程

    #import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutl ...

  5. C#中sealed关键字

    C#中sealed关键字 1. sealed关键字     当对一个类应用 sealed 修饰符时,此修饰符会阻止其他类从该类继承.类似于Java中final关键字.     在下面的示例中,类 B ...

  6. Linq to Entity中连接两个数据库时要注意的问题

    Linq to Entity中连接两个数据库时要注意的问题 今天大学同学问了我一个问题,Linq to Entity中连接两个数据库时,报错“指定的 LINQ 表达式包含对与不同上下文关联的查询的引用 ...

  7. cocos2d的ARC开启

    ARC,官方解释是Automatic Reference Counting,是Apple公司从iOS5开始为开发者新添加的一个功能. 相信很多写移动开发,可能不只是移动开发的人都深有体会,创建一个对象 ...

  8. zTree实现删除树节点

    zTree实现删除树节点 1.实现源码 <!DOCTYPE html> <html> <head> <title>zTree实现基本树</titl ...

  9. libguestfs手册(3): virt命令

    guestmount root# guestmount -a ubuntutest1.img -m /dev/sda1 ubuntutestp1 root# cd ubuntutestp1/root: ...

  10. [Python设计模式] 第28章 男人和女人——访问者模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下不同情况: 男人成功时,背后多半有一个伟大的女人: 女人成功 ...