各位同学,大家晚上好,我是Awe,大学退学,目前在北京从事前端的工作,因为之前入门前端就开始使用Vuejs,算有一些爬坑经历。受豪情大大委托,花大家一点时间和大家聊聊我使用Vuejs的经历,主要内容就是讲讲Vuejs组件化相关的一些点;因为我能力有限,可能会有理解不到位的地方,希望群里大神指正,一起交流。
前端开发的现状
随着互联网设备的带宽和硬件的发展,web的应用的交互越来越重。
想开发的复杂交互的网站怎么办?
没关系,一大波前端框架来拯救你。
这个是我找到的一张图,是一些最近几年出现的前端框架。左上角的有Angular、Ember、BackBone、Knockout,还有今天即将安利的Vue;这里其他位置我认识的还有Meteor,感觉它是非常未来的框架,实时的特性非常excited,还有右上的Polymer、React。
怎么选择前端框架
我只是想安静地写前端
作为一个选择恐惧症,面临这么多选择的时候是很苦恼的
其实作为一个安静的前端,只是需要一个不需要太多时间成本来学习,快速的上手开发业务,并使得项目的开发和用户体验有可见的提升(用户给好评,老板很满意,升值加薪,走上人生巅峰!)。
那么我们就进入正题安利 Vue.js
Vue.js
Reactive Components for Modern Web Interfaces
数据驱动的组件,为现代化的 Web 界面而生
最新版本是 2.0 23k star
尤小右 Evan You
@youyuxi
多说 / Google / Meteor
发布于 2014 年 2 月
Vue.js 主要是干啥的?
它是一个 MVVM 前端框架
Model / View / ViewModel
我们不需要撰写任何 DOM 操作代码:被绑定增强的 HTML 模板是底层数据状态的声明式的映射,数据不过是普通 JavaScript 对象。我们的视图完全由数据驱动。
数据驱动?
演示这个例子: http://codepen.io/awee/pen/grjGbB
Vue.js 怎么实现的?
vue将普通的对象的属性通过
Object.defineProperty
转换为ES5特性之一的 getter/setter
,模板中每个指令/数据绑定都有一个对应的 watcher 对象, 当修改对象值的时,首先会触发属性的setter
,在setter
被调用时,会触发 watcher 重新计算 ,也就会导致它的关联指令更新 DOM。
Vue.js 的核心是一个响应的数据绑定系统,它让数据与 DOM 保持同步非常简单。在使用 jQuery 手工操作 DOM 时,我们的代码常常是命令式的、重复的与易错的。Vue.js 拥抱数据驱动的视图概念。通俗地讲,它意味着我们在普通 HTML 模板中使用特殊的语法将 DOM “绑定”到底层数据。一旦创建了绑定,DOM 将与数据保持同步。每当修改了数据,DOM 便相应地更新。这样我们应用中的逻辑就几乎都是直接修改数据了,不必与 DOM 更新搅在一起。这让我们的代码更容易撰写、理解与维护。
为什么要用Vue.js
- 清晰简单的API让业务代码更好的组织
- 解决了前端交互复杂而带来的性能问题
- 健全、颜值高的官⽅⽂档
- 完整的开发生态链
那些公司在用Vue.js
Google,Facebook,Airbnb,
微博 小米 阿里巴巴,百度,饿了么,58
前端组件化?
前端组件化得核心思路就是将一个巨大复制的东西拆分成粒度合理的小东西。
使用Vue单文件组件
在典型的 Vue.js 项目中,我们会把界面拆分为多个小组件,每个组件在同一地方封装它的 CSS 样式,模板和 JavaScript 定义。
使用vue官方的vue-loader + webpack还可以给其中的style template 和 script使用各种预、后处理器来编译,比如style可以使用sass、less、postcss等,在掘金 template是写的jade,非常怀念写jade(pug)的日子。至于 script 当然推荐是来写 ES6(+7)。
这里不少功能涉及到webpack相关配置,不过vue有官方脚手架工具vue-cli,它也有几个模板和模板内的配置选项,可快速搭适合建你的应用开发环境。
使用 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>
{{msg}}
</div>
</template>
<script>
export default {
props: {
msg: String
}
}
</script>
组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。
“prop” 是组件数据的一个字段,期望从父组件传下来。子组件需要显式地用 props 选项,props的声明可以是简单的数组方式,也可以是对象方式,可以包含类型验证和默认值。
props: ['msg', 'otherMsg']
props: {
msg: String,
otherMsg: {
type: String,
default () {
return 'it's default str'
}
}
}
使用 $emit 传递事件
<!-- 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>
我们先来看看这两段代码,这个两个是一个简单的父子组件的事件通信例子...
Vue 在它的组件系统中实现了一个用于组件树中通信的自定义事件的接口,任何一Vue实例都是一个事件节点,我们可以使用$emit在它上面触发事件,和DOM事件类似,Vue的事件是冒泡向上传递,不过不同的是,会在第一次触发回调之后就停止冒泡,当然你也可以在回调中 return true
就会继续向上传递。
使用 Slot 分发内容
<!-- 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>
<!-- 渲染结果 -->
<div>
<div>
<h1>This is my component!</h1>
<p>This is some original content</p>
</div>
</div>
slot 是Vue.js 的内容分发 API,参照了当前 Web 组件规范草稿,使用特殊的 <slot> 元素作为原始内容的插槽
编译作用域
<child>
<p>{{ msg }}<p>
</child>
这里的 msg
就是在父组件的作用域内的数据
父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译
具名 Slot
子组件:
<div>
<slot name="one"></slot>
<slot></slot>
<slot name="two"></slot>
</div>
父组件模板:
<multi-insertion>
<p slot="one">One</p>
<p slot="two">Two</p>
<p>Default A</p>
</multi-insertion>
渲染结果
<div>
<p slot="one">One</p>
<p>Default A</p>
<p slot="two">Two</p>
</div>
单个 Slot应该很好理解,那我们来看看vue是如何处理多个slot插入的。我们可以看到在子组件的 slot 中多了 一个name的属性,与它相对应的就是 父子间中DOM 的 slot属性,Vue 通过匹配他们就能准确的把内容分发到指定位置,对于父组件中没有指定name DOM,它就会被放置在子组件中匿名 slot 的位置。
注意事项
避免片段实例(Fragment Instance)
不这么写模板:
<div>root node 1</div>
<div>root node 2</div>
推荐这么写:
<div>
I have a single root node!
<div>node 1</div>
<div>node 2</div>
</div>
因为在使用 template 选项时,模板的内容将替换实例的挂载元素。因而推荐模板的顶级元素始终是单个元素。
下面几种情况会让实例变成一个片断实例:
- 模板包含多个顶级元素。
- 模板只包含普通文本。
- 模板只包含其它组件(其它组件可能是一个片段实例)。
- 模板只包含一个元素指令,如 <partial> 或 vue-router 的 <router-view>。
- 模板根节点有一个流程控制指令,如 v-if 或 v-for。
<!-- 不可以,因为没有根元素 -->
<example v-show="ok" transition="fade"></example>
<!-- props 可以 -->
<example :prop="someData"></example>
<!-- 流程控制可以,但是不能有过渡 -->
<example v-if="ok"></example>
那么为什么说要避免片段实例呢?
- 无法通过 this.$el 获取组件内的顶级节点
- 组件元素上的非流程控制指令,非 prop 特性和过渡将被忽略
避免使用prop 的.sync 父子组件间双向绑定
goodbye.once
goodbye.sync
在即将发布的 Vuejs 2.0中 .once, .sync 修饰符已经确认被废弃
props 会变为单向绑定,子组件不能直接操作父组件的数据属性,应当使用事件$emit 来进行组件间数据的通信
总结
以上讲了一些基础的Vue组件的功能,使用Vue开发SPA还的有重要的路由Vue-router和Flux的Vue实现Vuex,在实际开发中还需要了解Vue的指令、过滤器、生命周期等,结合Vue的文档也能很好理解,也推荐在github 通过 vue-awesome看一写优秀的第三方组件和插件的代码,相信能增长不少Vue开发的经验。
More
官网:http://vuejs.org/
论坛:http://forum.vuejs.org/
资源:https://github.com/vuejs/awesome-vue