Vue.js前端组件化实践

Radon UI

龙佳文

Awe

 hilongjw

来自四川,罗辑思维前端切图仔,曾在百词斩、掘金打酱油
不专业的前端,玩过                      

hilongjw@gmail.com

http://vue.bood.in

前端开发的现状

JQuey 又不是不能用

怎么选择前端框架

我只是想安静地写前端

前端 🐶 的理想

选择 Vue.js

Vue.js是什么?

“数据驱动的组件,为现代化的 Web 界面而生”

 

Vue.js的数据驱动 + 组件化思路是现代 Web 应用的优秀的解决方案。 让你可以通过简单而灵活的 API 创建由数据驱动的 UI 组件。

尤雨溪 Evan You
@youyuxi

多说 / Google / Meteor

发布于 2014 年 2 月

Vue.js 主要是干啥的?

它是一个 MVVM 前端框架
Model / View / ViewModel
 

我们不需要撰写任何 DOM 操作代码:被绑定增强的 HTML 模板是底层数据状态的声明式的映射,数据不过是普通 JavaScript 对象。我们的视图完全由数据驱动。

数据驱动?

我们不需要撰写任何 DOM 操作代码:被绑定增强的 HTML 模板是底层数据状态的声明式的映射,数据不过是普通 JavaScript 对象。我们的视图完全由数据驱动。

Vue.js 怎么实现的?

vue将普通的对象的属性通过`Object.defineProperty`转换为 `getter/setter`,并给相应的DOM绑定这个属性的`watcher`, 当修改对象值的时,首先会触发属性的`setter`,在` setter `被调用时,会触发 watcher 重新计算 ,也就会导致它的关联指令更新 DOM。

哪些公司在用Vue.js ?

Google,Facebook,Airbnb,
微博 小米 阿里巴巴,百度,饿了么,58,掘金

当然,还有罗辑思维

前端组件化?

  • 提高开发效率
  • 降低了维护成本

拆分一个庞大的业务成多个单一功能的组件

使用Vue单文件组件

Vue.js的单文件组件在同一地方封装它的 CSS 样式,模板和 JavaScript 定义

使用Vue单文件组件

就像拼乐高一样写前端

颜色: CSS 样式

形状:HTML 模板

动作:JavaScript 脚本

使用 Props 传递数据

组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。
<!-- parent.vue -->
<template>
    <div>
        <!-- 默认为单向绑定 -->
        <child :msg="parentMsg"></child>
    </div>
</template>
<script>
import child from './child'
export default {
    data () {
        return {
            parentMsg: 'some words'
        }
    },
    components: {
        child
    }
}
</script>
<!-- child.vue -->
<template>
    <div>
        
    </div>
</template>

<script>
    export default {
        props: {
           parentMsg: String
        }
    }
</script>

使用 Props 传递数据

props就像水管一样

- 显式声明

- 单项数据流

使用 $emit 传递事件

Vue 实例实现了一个自定义事件接口,用于在组件树中通信
<!-- parent.vue -->
<template>
    <div>
        <child @child-ready="handler"></child>
    </div>
</template>
<script>
import child from './child'
export default {
    components: {
        child
    },
    methods: {
        handler (msg) {
            console.log(msg)
        } 
    }
}
</script>
<!-- child.vue -->
<template>
    <div>
        it's child.vue
    </div>
</template>

<script>
    export default {
        ready () {
            this.sayReady()
        },
        methods: {
            sayReady () {
                this.$emit('child-ready', 'Hello!')
            }
        }
    }
</script>

使用 $emit 传递事件

我找不到比喻了

使用 Slot 分发内容

slot 是Vue.js 的内容分发 API,参照了当前 Web 组件规范草稿,使用特殊的 <slot> 元素作为原始内容的插槽
<!-- 渲染结果 -->
<div>
    <div>
      <h1>This is my component!</h1>
      <p>This is some original content</p>
    </div>
</div>
<!-- parent.vue -->
<template>
    <div>
        <child>
          <p>This is some original content</p>
        </child>
    </div>
</template>
<script>
import child from './child'
export default {
    components: {
        child
    }
}
</script>

<!-- child.vue -->
<template>
    <div>
      <h1>This is my component!</h1>
      <slot>
        如果没有分发内容则显示我。
      </slot>
    </div>
</template>


注意事项

  • 避免片段实例(Fragment Instance)
  • 避免使用prop 的.sync 父子组件间双向绑定

避免片段实例(Fragment Instance)

<template>
    <div>root node 1</div>
    <div>root node 2</div>
</template>
<template>
    <div>
      <div>node 1</div>
      <div>node 2</div>
    </div>
</template>

不这么写

推荐这么写

避免片段实例(Fragment Instance)

- 模板包含多个顶级元素。
- 模板只包含普通文本。
- 模板只包含其它组件(其它组件可能是一个片段实例)。
- 模板只包含一个元素指令,如 <partial> 或 vue-router 的 <router-view>。
- 模板根节点有一个流程控制指令,如 v-if 或 v-for。

 

  • 无法通过 this.$el 获取组件内的顶级节点
  • 组件元素上的非流程控制指令,非 prop 特性和过渡将被忽略

那么为什么说要避免片段实例呢?

<!-- 不可以,因为没有根元素 -->
<example v-show="ok" transition="fade"></example>

<!-- props 可以 -->
<example :prop="someData"></example>

<!-- 流程控制可以,但是不能有过渡 -->
<example v-if="ok"></example>

避免使用prop 的.sync 父子组件间双向绑定

在即将发布的 Vuejs 2.0中 .once, .sync 修饰符已经确认被废弃

props 会变为单向绑定,子组件不能直接操作父组件的数据属性,应当使用事件$emit 来进行组件间数据的通信

goodbye.once
goodbye.sync

Radon UI

氡(radon)是一种化学元素,符号为Rn,原子序为86,属于稀有气体,无色、无臭、无味,具放射性

Radon UI 是罗辑思维前端团队开发的一款基于Vuejs的前端组件库

氡是惰性气体,但同时它是最活跃的惰性气体。我们希望使用组件化来简化产品业务的开发,同时也希望更多开发者能利用组件化来“偷懒”

技术栈

RadonUI

Basic
    - AudioPlayer 音频播放
    - Button 按钮
    - ButtonGroup 按钮组
    - DropButton 下拉
    - Card 卡片
    - CardGroup 卡片组
    - Chart 图表
    - Icon 图标
    - Table 表格
    - Tag 标签

Form
    - Textfield 文本输入框
    - NumberInput 数字输入框
    - Checkbox 复选框
    - Radio 单选框
    - Switch 开关
    - Slider 滑动输入
    - Select 选择器
    - Cascader 级联选择器
    - Editable 可编辑文本

Common
    - Alert 警告提示
    - Modal 对话框
    - Notification 通知
    - Progress 进度条
    - Timeline 时间线
    - Tooltip 对话框
    - Spin 加载中
    - Upload 上传

Navigation
    - Breadcrumb 面包屑
    - Pagination 分页

局部组件示例

<template>
    <div class="container">
        <p>
            <rd-text :textfield="form.username"></rd-text>
        </p>
        <p>
            <rd-text :textfield="form.info"></rd-text>
        </p>
        <p>
            <rd-text :textfield="form.warning"></rd-text>
        </p>
        <p>
            <rd-text :textfield="form.success"></rd-text>
        </p>
        <p>
            <rd-text :textfield="form.failed"></rd-text>
        </p>
    </div>
</template>

数据

form: {
    username: {
        value: '',
        placeHolder: 'input here',
        state: 'default',
        tip: ''
    },
    password: {
        value: '',
        placeHolder: '',
        state: 'default',
        tip: ''
    },
    info: {
        value: '',
        placeHolder: 'info',
        state: 'info',
        tip: ''
    },
    warning: {
        value: '',
        placeHolder: 'warning',
        state: 'warning',
        tip: ''
    },
    failed: {
        value: '',
        placeHolder: 'failed',
        state: 'failed',
        tip: ''
    },
    success: {
        value: '',
        placeHolder: 'success',
        state: 'success',
        tip: ''
    }
}

结果

全局组件

我们可能在任何一个业务中调用,比如常用的 confirm、alert、notification。这里就会跨组件调用启动相应的modal。

安装

import { RadonInstall } from 'radon-ui'
Vue.use(RadonInstall)
methods: {
    loginAction () {
        AV.User.logIn('admin', '123456')
            .then(function (loginedUser) {
                this.$Notification.success('登陆成功, 正在跳转...', '', 3000)
             }, function (error) {
                 this.$Notification.warning('登陆失败, 请再次尝试', '', 3000)
             })
    }
}

在任何子组件调用`this.$Notification.warning`来创建新的notification

使用

 Vue.prototype.$Notification = {
        remove (item, duration) {
            setTimeout(() => {
                $root.Notifications.$remove(item)
            }, duration)
        },
        success (title, content, duration) {
            let item = {
                type: 'success',
                title: title,
                content: content
            }
            $root.Notifications.push(item)
            if (duration) {
                this.remove(item, duration)
            }
        }
}

全局组件

Vue.js Resources

  • 官网:http://vuejs.org/
  • 论坛:http://forum.vuejs.org/
  • 资源:vue-awesome
  • 掘金:http://gold.xitu.io/tag/Vue.js

罗辑思维实验室

Thanks

Vue.js与前端组件化

By Awe

Vue.js与前端组件化

简单谈谈Vue.js的组件化编程

  • 2,193