Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用 单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。
Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
一、技术选型——为什么要使用Vue.js?
- 需求:实现一个通过选择、输入等方式创建项目,以及对项目进行管理、查看、编辑等操作的应用。应用中多处需要数据双向绑定
- 体验:单页应用(Single page application, SPA)用户体验好、快,接近原生应用的体验
- 效率:提供常用逻辑的封装实现,编程效率高
- 扩展:允许异步加载组件,并允许直接操作DOM(React需通过refs访问到特定DOM)
- 轻量:轻量级框架不带有侵略性API,对容器也没有依赖性,易于进行配置,易于通用,启动时间较短。允许我们同时使用习惯的jQuery.js/Zepto.js
二、项目构建——Vue单文件组件刮来的清新风
-
目录结构:
{ "src":{ "app":{ "index":["index.html", "index.js", "index.less"] }, "common":[]//js,image等 "components":{ "common":["cover.vue", "loading.vue", "toast.vue"],//通用组件 "home":["index.vue", "tab.vue", "list.vue"],//页面组件 //... //单文件组件同HTML文件一样,支持文件内写HTML、CSS、JS //但首页入口需要是常规的html、js } } }
-
组件化:
-
组件与模块的关系
-
组件化的粒度:本项目以文件夹划分页面,以UI层级及重用关系划分组件
-
-
小结
vue单文件组件将一个组件的结构、样式、逻辑集中在单个类HTML文件里,这在以前是需要通过服务端做拼接处理的,vue可以做到通过js处理,也可以通过打包工具的特定模块处理。
这样的内容集中降低了工程项目的碎片化,在编程过程中也可以避免写一个组件需要在html、css、js不同文件之间进行频繁切换。
三、代码结构——Vue的使用概览
-
使用入门
-
创建根实例
index.html
<div id="app"></div>
index.js
var app = new Vue().$mount('#app');
-
创建单文件组件
demo.vue
<template> <div>{{greeting}} world!</div> </template> <style> </style> <script> module.exports = { data: function(){ return { greeting: 'Hello' } } } </script>
-
挂载组件
index.js
var demo = require('demo.vue')//Webpack用require,ES6用import var app = new Vue({ components: { demo: demo } }).$mount('#app');
index.html
<div id="app"> <demo></demo> </div>
-
运行结果
<div id="app"> <div>Hello world!</div> </div>
-
-
实践中遇到的难点
-
数据双向绑定及监听变化:
-
数据双向绑定
双向数据绑定指的就是,绑定对象属性的改变到用户界面的变化的能力,反之亦然
双向:数据->视图、视图->数据
绑定:一个内容发生改变,另一个内容随之改变
demo.vue:一个典型的数据双向绑定示例
<template> <input type="text" v-bind:value="msg" v-on:input="changeMsg($event)"/> <div>{{msg}}</div> </template> <script> module.exports = { data: function(){ 'msg':'hello' }, methods: { changeMsg: function(e){ this.msg = e.target.value } } } </script>
Vue提供了
v-model
指令,对上面的代码简写如下
demo.vue:
v-model
指令示例<template> <input type="text" v-model="msg"/> <div>{{msg}}</div> </template> <script> module.exports = { data: function(){ 'msg':'hello' }, } </script>
目前为止,要在数据变化后执行逻辑,可以扩展
changeMsg
内容,或监听input
、change
事件。Vue提供了computed、watch两种方式来监听变化 -
computed
demo.vue:
computed
示例<template> <input type="text" v-model="msg"/> <div>{{msg}}</div> <div>{{rs}}</div> </template> <script> module.exports = { computed: { rs: function () { console.log(this.msg) return this.msg.split('').reverse().join('') } } } </script>
-
watch
demo.vue:
watch
示例<template> <input type="text" v-model="msg"/> <div>{{msg}}</div> <div>{{rs}}</div> </template> <script> module.exports = { data:function () { return { msg: 'hello', rs: 'olleh' } }, watch: { msg: function (val) { console.log(val) this.rs = val.split('').reverse().join('') } }, } </script>
-
computed与watch的异同
-
相同
受现代 Javascript 的限制(以及废弃
Object.observe
),Vue 不能检测到对象属性的添加或删除它们都只能观察到Vue实例对象(Vue实例中,
this
指向的对象)上存在的属性变化 -
不同
-
computed会自动给Vue实例对象上添加属性、覆盖已有属性,watch不会
-
computed中的方法只在被访问时执行,而不是在监听属性变化发生时立即执行
watch中的方法,在监听属性变化发生时立即执行
遇到
computed
监听无效,而watch
有效,原因往往出在这里 -
computed只关心
计算结果
,不限制多少个属性发生了变化,可以监听多个属性变化的结果计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。
watch只关心某个
属性变化
,发生变化则执行相应逻辑
-
-
-
-
单向数据流
-
Vue只封装了从父组件到子组件的通信方式:prop
props中包含父组件传入的属性、方法,这些内容会被添加到Vue组件的实例对象上
-
Vue建议的从子组件到父组件的通信方式:自定义事件
父组件提供一个自定义事件,用于接收子组件的信息,执行相应的函数
-
Vue补充的任意组件之间的通信方式:全局自定义事件(非父子组件通信)
-
设置一个新的Vue实例:
var bus = new Vue()
-
*建议将其挂在window上以供工程中全局使用
window.Bus = bus
-
在组件A中监听自定义事件
A.vue
<script> module.exports = { created: function(){ bus.$on('event-A', function (something) { // ... }) } } </script>
-
在组件B中触发自定义事件
B.vue
<script> module.exports = { created: function(){ bus.$emit('event-A', 'say something') } } </script>
-
-
-
生命周期
-
加入keep-alive后的生命周期
<template> <div id="parent"> <keep-alive> <component v-bind:is="nowChild"></component> </keep-alive> </div> </template> <script> var child1 = require('child1.vue') var child2 = require('child2.vue') module.exports = { data: function(){ return { nowChild: 'child1' } }, components:{ child1: child1, child2: child2 } } </script>
keep-alive
的子组件的基本生命周期同keep-alive
的父组件相似,但keep-alive
的子组件间切换,会触发activated
、deactivated
,而不会二次触发子组件的基本生命周期
-
四、体验优化——单页应用的用户体验
-
原则一:避免外链跳转
单页应用在单页内有一致的、流畅的用户体验。而单页与外链间的跳转,则会产生大量的计算、渲染,反而暴露出单页应用不“轻量”的缺点。因此,避免外链跳转,降低单页内存在的外链被点击的可能,有助于减少用户对明显体验差异的感知,一定程度上提高用户体验。
存在需要直接打开外链的情况,优化措施可以是在新窗口中打开。
-
原则二:优化首屏
上述“单页应用不 ‘轻量’ ”的问题,在首屏表现尤其明显。进入一个单页,往往是长时间的等待,情况类似于打开一个APP,先看到的一般是首屏图片、动画、引导、广告。
这里的优化也需要从技术、视图两方面入手。技术上采用服务端渲染、异步加载组件等方案,视图上则需要做到首屏尽快从一片空白变成有点东西。
-
原则三:使用缓存
单页应用可以充分实践MV*的理念,视图的更新反映了数据的更新。时间花销主要在于DOM渲染、逻辑运算、等待请求返回中,这些数据均可以被缓存以留到下次使用。
使用缓存是单页应用体验趋近原生应用的主要因素。