其余章节请看:javascript
vue 快速入门 系列html
Tip: vue 的基础应用分上下两篇,上篇是基础,下篇是应用。vue
在初步认识 vue一文中,咱们已经写了一个 vue 的 hello-world。对 vue 已经有了一个大概的印象。java
接下来咱们应该掌握 vue 的最基础知识,学会 vue 的基本应用。vuex
比较好的方法就是花个几天的时间将 vue 官网的基础篇尽可能走一遍,写一写例子。如下是 vue 2.x 的基础篇的目录:app
- 教程 2.x - 基础 - 安装 - 介绍 - Vue 实例 - 模板语法 - 计算属性和侦听器 - Class 与 Style 绑定 - 条件渲染 - 列表渲染 - 事件处理 - 表单输入绑定 - 组件基础
注:若是你像笔者同样,只有 jQuery 开发的经验,在看基础篇的过程种,确定会有不少的不懂之处,先看下去,把能看懂的先看完。框架
本文记录的是笔者当初看 vue 基础篇遇到的一些重要的、很差理解的知识点,算是基础篇的一个补充或笔记。dom
经过 new Vue()
来建立一个实例,其中 el 选项提供一个在页面上已存在的 DOM 元素做为 Vue 实例的挂载目标。函数
vue 实例会经历一系列的过程,好比数据侦测、模板编译、渲染到视图等,这就是 vue 实例的生命周期,vue 对外提供了生命周期的钩子函数,这就容许咱们在 vue 的各个阶段插入一些咱们的逻辑。工具
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src='vue.js'></script> </head> <body> <div id='app'> <p> {{message}} </p> </div> <script> // 经过 new Vue() 建立一个实例 var app = new Vue({ // 提供一个在页面上已存在的 DOM 元素做为 Vue 实例的挂载目标。 // 能够是 CSS 选择器,也能够是一个 HTMLElement 实例。 el: '#app', // Vue 实例的数据对象 data:{ message: 'hello', }, // 生命周期 created: function(){ console.log('实例被建立时触发') }, mounted: function(){ console.log('被挂载时触发') } }) </script> </body> </html>
Tip: 后续不在提供完整的代码,省略 head、body 等。
前文说过 vue 是声明式操做 dom 的框架。咱们只须要描述状态与 dom 之间的映射关系便可,状态到视图的转换,框架会帮咱们作,状态改变,视图也会自动更新。
而 vue 是经过模板来描述状态与 dom 之间的映射关系。因此模板的知识点稍微会多一点。
Vue 会尽量高效地渲染元素,一般会复用已有元素而不是从头开始渲染。
<div id='app'> <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template> <button @click='toggleHandle'>切换</button> </div> <script> var app = new Vue({ el: '#app', data:{ loginType: 'username', }, methods: { toggleHandle: function(){ this.loginType = this.loginType === 'username' ? 'email' : 'username'; } } }) </script>
在上面的代码中切换 loginType 将不会清除用户已经输入的内容。由于两个模板使用了相同的元素,<input>
不会被替换掉——仅仅是替换了它的 placeholder。若是须要表达“这两个元素是彻底独立的,不要复用它们”,只需添加一个具备惟一值的 key attribute 便可。
// 给 input 添加 key 属性 <input placeholder="Enter your username" key="username-input"> <input placeholder="Enter your email address" key="email-input">
v-if 是真正的条件渲染;v-show 只是简单地切换元素的 CSS property display;
例以下面这种场景,点击一个新建按钮,显示弹框组件(custom-dialog),弹框中有一些 input 输入框,若是须要每次点击新建,弹框组件整个都从新生成,则可使用 v-if;不然在弹框中输入了文字,经过 v-show 隐藏弹框,下次在显示时,可能以前输入的文字、错误信息都还存在。
<custom-dialog v-if='isShow'></custom-dialog> <button @click='clickHandle'>新建</button>
v-model:在表单控件或者组件上建立双向绑定。
应用在表单控件上,请看示例:
<div id='app'> <input v-model="message" placeholder="edit me"> <!-- {1} --> <!-- <input type="text" :value='message' @input='handleInput' placeholder="edit me"> // {2} --> <p>Message is: {{ message }}</p> </div> <script> var app = new Vue({ el: '#app', data:{ message: 'a' }, methods: { handleInput: function(e){ this.message = e.target.value } } }) </script>
v-model 本质上不过是语法糖。行{1} 的本质实际上是 行{2}。
v-model 在组件中的使用请看下文。
上面介绍的实际上是基础中最简单的部分。而组件是基础篇中最应该掌握的部分。
能够经过 Vue.component() 注册或获取全局组件。请看示例:
<div id='app'> <p>{{message}}</p> <button-counter></button-counter> <button-counter></button-counter> </div> <script> // 定义一个名为 button-counter 的新组件 Vue.component('button-counter', { // 一个组件的 data 选项必须是一个函数 // 这样,每一个组件的数据都是独立的 data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) var app = new Vue({ el: '#app', data: { message: 'hello' } }) </script>
组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>
。
由于组件是可复用的 Vue 实例,因此它们与 new Vue() 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
一般一个应用会以一棵嵌套的组件树的形式来组织,这样就能完成一个功能很复杂的页面。
props 用于接收来自父组件的数据。请看示例:
<div id='app'> <!-- <button-counter v-bind:msg='message'></button-counter> --> <!-- 缩写 --> <button-counter :msg='message'></button-counter> </div> <script> Vue.component('button-counter', { props: ['msg'], template: `<div> 来自父组件的信息: {{msg}} </div>` }) var app = new Vue({ el: '#app', data: { message: 'hello' } }) </script>
v-bind:msg='message'
能够缩写成 :msg='message'
v-on 用在自定义元素组件上时,能够监听子组件触发的自定义事件。请看示例:
<div id='app'> number: {{count}} <!-- 父组件给子组件注册了事件 chang-count,事件的回调方法是 changCount --> <button-counter v-on:chang-count='changCount'></button-counter> </div> <script> Vue.component('button-counter', { data: function(){ return { num: 10 } }, template: `<div> <!-- v-on:click 缩写 @click --> <button @click='triggerHandle'>触发</button> </div>`, methods: { triggerHandle: function(){ // 触发事件 this.$emit('chang-count', this.num++); } } }) var app = new Vue({ el: '#app', data: { count: 0 }, methods: { changCount: function(v){ this.count = v; } } }) </script>
v-on 的语法糖是 @。
v-model 在表单控件或者组件上建立双向绑定。上文已经介绍了如何在表单上使用。而用在组件上,原理是相同的。请看示例:
<div id='app'> number: {{count}} <!-- 父组件给子组件注册了事件 chang-count,事件的回调方法是 changCount --> <custom-input v-model="count"></custom-input> <!-- {20} --> <!-- <custom-input :value="count" @input='changCount'></custom-input> --> <!-- {21} --> </div> <script> Vue.component('custom-input', { props: ['value'], data: function(){ return { msg: this.value } }, template: `<div> <input :value='msg' @input='inputHandle'/> </div>`, methods: { inputHandle: function(e){ this.msg = e.target.value // 触发事件 this.$emit('input', this.msg); } } }) var app = new Vue({ el: '#app', data: { count: 10 }, methods: { changCount: function(v){ this.count = v; } } }) </script>
行{20} 与 行{21} 等效。
props 用于父组件给子组件传递数据;
子组件给父组件传递数据能够经过事件(例如 vm.$emit());
但非父子之间如何通讯?
对于 $dispatch 和 $broadcast 最简单的升级方式就是:经过使用事件中心,容许组件自由交流,不管组件处于组件树的哪一层。因为 Vue 实例实现了一个事件分发接口,你能够经过实例化一个空的 Vue 实例来实现这个目的。—— vue 官网
简单来讲,就是使用一个空的 Vue 实例做为中央事件总线,能够帮助咱们解决兄弟组件之间的通讯。请看示例:
<div id='app'> <component-a></component-a> <component-b></component-b> </div> <script> Vue.component('componentA', { data: function(){ return { msg: '我是 componentA' } }, template: `<div> message: {{msg}} <button @click='send'>给兄弟发送数据</button> </div>`, created: function(){ var that = this; // 给事件中心注册事件 bus.$on('on-messageA', function(msg){ console.log('received: ' + msg) that.msg = msg }) }, methods: { send: function(e){ // 给事件中心触发事件 bus.$emit('on-messageB', 'from componentA') } } }) Vue.component('componentB', { data: function(){ return { msg: '我是 componentB' } }, template: `<div> message: {{msg}} <button @click='send'>给兄弟发送数据</button> </div>`, created: function(){ var that = this; bus.$on('on-messageB', function(msg){ that.msg = msg }) }, methods: { send: function(e){ console.log('from componentB') bus.$emit('on-messageA', 'from componentB') } } }) // 事件中心 var bus = new Vue() var app = new Vue({ el: '#app' }) </script>
这个例子中定义了两个组件,首先用 new Vue() 建立一个中央事件总线(bus),相似一个中介,接着组件(componentA、componentB)给 bus 注册事件,当点击按钮时触发 bus 的事件,并传递数据。
这种方式其实能够实现任何组件之间的通讯,包括父子、兄弟、跨级等,由于在 bus 看来,我(bus)就是中介,其余组件只是个人客户。
Tip:项目比较大时,一般会使用 vuex,后续会介绍 vuex 这个状态管理工具。
vm.$parent 能够取得父组件;经过 vm.$children 取得当前实例的直接子组件,并经过 vm.$refs 取得指定子组件。请看示例:
<div id='app'> <component-a ref='comA'></component-a> <component-a ref='comB'></component-a> </div> <script> Vue.component('componentA', { data: function(){ return { msg: 'hello' } }, template: `<div> message: {{msg}} </div>`, watch: { msg: function(){ // 经过 vm.$parent 取得父组件 console.log(this.$parent.name) // 经过 ref 指定子组件索引名称 // 经过 vm.$refs 取得子组件 console.log(this.$parent.$refs.comA.msg) } } }) var app = new Vue({ el: '#app', data: { name: '我是父' } }) // 经过 vm.$children 直接更改子组件的数据 console.log(app.$children[1].msg = 'hello2') </script>
插槽也能够叫 slot 内容分发。请看示例:
<div id='app'> <component-a ref='comA'> <p>子元素的 slot 插槽将会被我替换</p> <!-- {30} --> </component-a> </div> <script> Vue.component('componentA', { template: `<div> hello <slot></slot> </div>`, }) var app = new Vue({ el: '#app', }) </script>
咱们在组件A中定义了一个插槽(<slot></slot>
),父组件中的 p(行{30})将会替换子组件的 slot。
Tip: props、事件以及 slot 内容分发构成了 Vue 组件的 3 个 API 来源。
做用域插槽,概念不太好理解,直接看示例:
<div id='app'> <component-a> <!-- slot-scope 用于将元素或组件表示为做用域插槽 --> <template slot-scope='props'> <p>来自父组件</p> <p>{{props.msg}}</p> </template> </component-a> </div> <script> Vue.component('componentA', { data: function(){ return { msg: '来自子组件的内容' } }, template: `<div> hello <slot :msg='msg'></slot> </div>`, }) var app = new Vue({ el: '#app', }) </script>
做用域插槽,就是让插槽的内容可以访问子组件中才有的数据。
注:因为笔者的 vue 是 2.5.x,因此用的是 slot-scope。而在 2.6.x 中,scope、slot和slot-scope 都推荐使用 v-scope。
Vue.js 容许你自定义过滤器,可被用于一些常见的文本格式化。请看示例:
<div id='app'> <p>{{msg | toUpperCase}}</p> </div> <script> var app = new Vue({ el: '#app', data: { msg: 'hello' }, // 过滤器 filters: { toUpperCase: function(v){ return v.toUpperCase() } } }) </script>
在有些状况下,咱们可能须要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,由于子组件能够变动父组件,且在父组件和子组件都没有明显的变动来源。这也是为何咱们推荐以
update:myPropName
的模式触发事件取而代之。—— vue 官网
简单来讲,父组件给子组件传递了一个属性,子组件不要直接更改父组件的这个属性,而应该通知父组件,让父组件本身去更改这个属性。请看示例:
<div id='app'> 父组件: name = {{name}} <!-- 当在父级组件监听事件的时候,咱们能够经过 $event 访问到被抛出的这个值 --> <custom :name="name" v-on:update:name="updateName($event)"></custom> <!-- {40} --> <!-- 相等 --> <!-- <custom :name.sync="name"></custom> --> <!-- {41} --> </div> <script> let seed = 1; Vue.component('custom', { props: ['name'], template: ` <div> <p>子组件: name = {{name}}</p> <p><button @click='handleClick'>change name</button></p> </div> `, methods: { handleClick: function(){ // update:name 就是一个事件名 // 改成 update--name 也能够 this.$emit('update:name', seed++) } } }) const app = new Vue({ el: '#app', data: { name: 'hello' }, methods: { updateName: function(v){ this.name = v } } }) </script>
为了方便起见,咱们为这种模式(行{40})提供一个缩写,即 .sync 修饰符(行{41})
其余章节请看: