Vue.js 项目实战
Vue 开发入门
Vue 是一个专注于构建 Web 用户界面的 JavaScript 库。
1.1 为什么需要另外一个前端框架
Vue 易用、灵活、速度快、还提供了许多功能和可选工具,这使得开发者能够快速地构建一个现代 web 应用。Vue 的作者尤雨溪将其称为渐进式框架。
- Vue 遵循渐进增量的设计原则,其核心库专注于用户界面,使得现有的项目可以方便地集成使用 Vue。
- Vue 既可以构建出很小的原型,又可以构建出复杂的大型 web 应用。
- Vue 非常容易上手 —— 初学者能轻松掌握 vue,而已经熟悉 Vue 的开发者则可以在实际项目中快速发挥出它的作用。
Vue 整体上遵循 MVVM(Model-View-ViewModel,模型 - 视图 -视图模型)架构,也就是说 View(用户界面或视图)和 Model(数据)是独立的,ViewModel(Vue)是 View 和 Model 交互的桥梁。Vue 对 View 和 Model 之间的更新操作做了自动化处理,并且已经为开发者进行了优化。
Vue 还吸收了其他类似框架( 如 React、Angular、Polymer)的精华。
Vue 核心功能概述
- 一个响应式的数据系统,能通过轻量级的虚拟 DOM 引擎和最少的优化工作来自动更新用户界面。
- 灵活的视图声明,包括优雅友好的 HTML 模板、JSX(在 JavaScript 中编写 HTML 的技术)以及 hyperscript 渲染函数(完全使用 JavaScript )。
- 由可维护、可复用组件组成的组件化用户界面。
- 官方的组件库提供了路由、状态管理、脚手架以及更多高级功能,使 Vue 成为了一个灵活且功能完善的前端框架。
Weex、NativeScript
1.1.2 兼容性需求
Vue 不支持 Internet Explorer 8 以下版本,因为 Vue 使用了 JavaScript 中相对较新的特性,比如 Object.defineProperty,
而它们在老版本的浏览器中是无法 polyfill 的。
编译器 Babel ,它编译过的代码可以很好地运行在老版本浏览器中。
1.3 创建一个应用
整个库都是基于 Vue 实例的,而实例是 View 和 Model(数据)交互的桥梁。因此需要创建一个新的 Vue 实例来启动应用。
1 | // 创建 Vue 实例 |
使用关键字 new 调用 Vue 构造器创建了一个新的实例。Vue 构造器有一个参数 —— option 对象。该参数可以携带多个属性(称为选项)。
通过 el 选项,我们使用 CSS 选择器告知 Vue 将实例添加(挂载)到 Web 页面的哪个 DOM 元素中。
也可以 Vue 实例的 $mount 的方法代替el选项:
app.$mount(‘#root’)。
1 | var app = new Vue({ |
Vue 实例的大多数特殊方法和属性都是以美元符号($)开头的。
在单个 Web 页面中,开发者可以添加任意多个 Vue 应用。只需要为每个应用创建出
新的 Vue 实例并挂载到不同的 DOM 元素即可。当想要将 Vue 集成到已有的项目中时,这非常方便。
Vue 开发者工具
Vue 有一个官方调试工具,在 Chrome 中以扩展的方式呈现,名为 Vue.js devtools。通过该工具可以看到应用的运行情况,这有助于调试代码
。可以在 Chrome 网上应用商品下载;如果使用 Firefox,则可以到 Firefox 附加组件 下载。
使用 Chrome 版本的话,还需要进行额外的设置。在扩展设置中,启用 Allow access to file URLs 选项。这样调试工具就能在从本地磁盘打开的 Web 页面上检测 Vue 了。
可以将 devtools 选项卡拖放到喜欢的位置。建议将其放在靠前的位置,因为当 Vue 不处于开发模式或没有运行时,该选项卡在页面中是隐藏起来的
可以通过 name 选项修改 Vue 实例的名字:
1 | var app = new Vue({ |
当一个页面中有多个 Vue 实例时,这有助于直观地在开发者工具中找到具体的某个实例。
1.4 借助模板实现 DOM 的动态性
模板是描述 View 最简单的方法,只需要少量额外的语法就能轻松实现 DOM 的动态更新。
1.4.1 文本显示
文本插值用于在 Web 页面中显示动态的文本。文本插值的语法是在双花括号内包含单个任意类型的 JavaScript 表达式。当 Vue 处理模板时,该 JavaScript 表达式的结果将会替换掉双花括号标签。
1 | <div id="root"> |
DOM 和 数据连通了
1 | app.message = 'Awesome!' |
数据绑定,每当数据有改变时, Vue 都能够自动更新 DOM,Vue 框架中包含一个非常强大且高效的响应系统,能对所有的数据进行跟踪,并且能在数据发生改变时按需自动更新 View。
1.4.2 利用指令添加基本的交互
Vue 中所有的指令命名都是带 V- 前缀的,并遵循短横线分隔式(kebab-case)语法。这个意味着要用短横线将单词分开。HTML 属性是不区分大小写的。
1 | <div id="root"> |
双向数据绑定 v-model
项目 1:Markdown 笔记
v-model 指令不限于文本输入使用。它同样可以用于其他元素,例如勾选框、单选按钮,甚至自定义组件。
2.1.3 预览面板
计算属性 通过它可以定义一个新的属性,而该属性可以结合任意多个属性,并做相关转换操作。
- 计算属性的值基于它的依赖进行缓存,因此如果没有必要是不会重新运行函数的,从而有效防止无用计算;
- 当函数中用到的某个属性发生了改变,计算属性的值也会根据需要自动更新;
- 计算属性可以如其他普通属性一起使用(可以在其他计算属性中使用计算属性);
- 计算属性只有真正用于应用中时,才会进行计算操作。
元素中的任意内容将被 v-html 指令的值替代。可以利用这一点来放置占位符内容。
对于文本插值,v-text 是一个与 v-html 等效的指令。它的行为与 v-html 类似,只不过会对 HTML 标签做转译处理,形同典型的文本插值。2.1.4 保存笔记
localStorage 保存笔记
侦听改变
监听器 watch 选项是一个字典,把被侦听属性的名字作为键,把侦听选项对象作为值。这个对象必须要一个 handler 属性,该属性可以是一个函数,也可以是一个方法的名字。这个处理函数将接收两个参数:被侦听属性的新值和旧值。
1 | new Vue({ |
还有另外两个选项可以和 handler 一起使用。
- deep 是一个布尔类型,告诉 Vue 以递归的方式侦听嵌套内部值的变化。
- immediate 也是一个布尔类型,会立即触发调用处理函数,而不用等到属性第一次变化时才调用。
1 | watch: { |
保存笔记
1 | localStorage.setItem('content',val) |
复用方法
良好的编程准则之一:不要重复自己(DRY),也称为一次仅且一次。开发者应该遵守这个准则。因此可以把一些逻辑写在
可复用的函数里面:methods。
1 | new Vue({ |
访问 Vue 实例
在 methods 内部,可以通过 this 关键字访问 Vue 实例,还可以访问 Vue 实例的其他属性或特殊函数。
基本上可以在任意函数(方法、处理函数或其他钩子)其中使用 this 关键字访问 Vue 实例。 生命周期钩子生命周期钩子
每个 Vue 实例都严格遵守一个生命周期,包括多个环节:创建、挂载到页面、更新,最终被销毁。例如,在创建实例阶段,Vue 会将实例数据变成响应式数据。
钩子是一组特殊的函数,会在某个时间点被自动调用。这就允许我们自定义框架的逻辑。例如在创建 Vue 实例时调用一个方法。在每个环节之中或之前,有很多钩子可以用于执行逻辑。
- beforeCreate:在 Vue 实例被创建时(例如使用 new Vue({ }))、完成其他事项之前调用。
- created:在实例准备就绪之后调用。注意,此时实例还没有挂载到 DOM 中。
- beforeMount:在挂载(添加)实例到 web 页面之前调用。
- mounted:在实例被挂载到页面并且 DOM 可见时调用。
- beforeUpdate:当实例需要更新时(一般来说,是当某个数据或计算属性发生改变时)调用。
- updated:在把数据变化应用到模板之后调用。注意此时 DOM 可能还没有更新。
- beforeDestroy:在实例销毁之前调用。
- destroyed:在实例完全销毁之后调用。
在数据中直接初始化
1 | new Vue({ |
2.2 多条笔记
2.2.1 笔记列表
添加新建笔记的方法
每一条笔记都是具体如下数据的对象。
- id :笔记的唯一标识符。
- title :笔记的标题,用来显示在笔记列表中。
- content : 笔记的 Markdown 格式内容。
- created : 笔记创建的日期。
- favorite : 这是一个布尔型,用于表示是否收藏了笔记,已收藏的笔记显示在笔记列表的顶部。
选择当前时间(也就是从 1970 年 1 月 1 日 00:00:00 UTC 开始经过的毫秒数)作为区分笔记的唯一标识符,这是一种不错的方式。
用 v-on 实现按钮的单击事件
1 | <button v-on:click="addNote"></button> |
绑定事件 v-on: 简写 @
1 | <button @:click="addNote"></button> |
用 v-bind 绑定属性
1 | <button v-bind:title="notes.length + ' note(s) already'"> |
属性绑定 v-bind: 简写 :
1 | <button :title="notes.length + ' note(s) already'"> |
用 v-for 显示列表
1 | <div class="notes"> |
2.2.2 选择一条笔记
动态 CSS 类
1 | <div :class= "{ one: note === selectedNote }"></div> |
条件指令 v-if
v-else-if
v-else
侦听器默认值侦听目标对象的直接变化:赋值、数组添加、删除、移动。
例如:
1 | // 赋值 |
操作不会触发侦听器
1 | // 给某个属性或者嵌套对象赋值 |
这种情况下,需要在侦听器上添加 deep 选项
1 | watch: { |
按照收藏、时间排序
1 | computed: { |
创建日期过滤器
(1)引入 momentjs
1 | <script src="https://unpkg.com/moment"></script> |
(2) 使用 Vue.filter 全局方法(不在 Vue 实例的创建代码中,比如位于文件开头)注册过滤器
1 | Vue.filter('date',time=> mement(time).format('DD/MM/YY, HH:mm')) |
1 | <span class="value">{{ selectedNote.created | date }}</span> |
文本统计
1 | // 计算换行符的格式 |
项目 2:城堡决斗游戏
在 Vue 实例构造器之后,添加一个事件监听器到 window 对象中,监听浏览器窗口大小的变化。
1 | // 窗口大小变化的处理 |
3.3.3 万能的组件
组件是构建应用的基础模块,是 Vue 应用的核心概念。组件是视图的一个个小部分,因此相对来说应用比较小、可复用,并且尽可能地自给自足。采用组件构建应用有助于应用的维护和升级,特别是当应用规模变大之后。实际上,这已经成为了高效、可控地开发大型 Web 应用的标准方法。
可以使用全局函数 Vue.component() 来注册组件。该函数接受两个参数:一个是注册组件的名称,另一个则是组件的定义对象本身,它与 Vue 实例使用相同的选项。
1 | Vue.component('top-bar',{ |
使用 prop 进行父组件到子组件的通信
利用 props 选型可以将 prop 添加到组件中。
1 | Vue.component('top-bar',{ |
模板中的 prop
通过 players prop 显示玩家姓名。
1 | template:` |
在组件上监听原生事件
为监听到组件的 click 事件,需要对 v-on 指令使用 .native 修饰符。
1 | <card :def="testCard" @click.navtive="handlePlay"> |
使用自定义事件进行子组件到父组件的通信
在组件内部,使用 $emit 这个特殊方法触发的事件可以被父组件捕获到。该方法接收一个固定的参数,即事件类型:
1 | this.$emit('play','orange',42) |
可以使用名为 $on 的特殊方法监听自定义事件:
1 | this.$on('play', () => { |
hand 组件的动画过度效果
动画效果:CSS 过渡结合 <transition>
组件。当添加或移除元素时,使用 v-if 或 v-show 指令来帮助实现 CSS 过渡。
1 | // 应用状态集合 |
1 | <transition> |
<transition>
特殊组件不会显示在 DOM 中。
当元素被添加到 DOM 时(进入阶段),<transition>
组件会自动将下列 CSS 类应用到元素中。
v-enter-active: 当进入过渡状态被激活时,会应用该类。在元素插入 DOM 之前,添加该类到元素中,并在动画结束时移除它。应该在这类中添加一些 transition CSS 属性并定义其过渡时长。
v-enter: 元素进入过渡的开始状态。在元素插入 DOM 之前,添加该类到元素中,并在元素被插入的下一帧移除。例如,你可以在这个类中设置透明为 0。
v-enter-to: 元素进入过渡的结束状态。在元素插入 DOM 后的下一帧添加,同时 v-enter 被移除。当动画完成后,v-enter-to 会被移除。
当元素从 DOM 中移除时(离开阶段),<transition>
组件会自动将下列 CSS 类应用到元素中。
v-leave-active:当离开过渡状态被激活时,会应用该类。当离开过渡触发时,添加该类到元素中,并在从 DOM 中移除元素时移除它。应该在这个类中添加一些 transition CSS 属性并定义其过渡时长。
v-leave:元素被移除时的开始状态。当离开过渡触发时,添加该类到元素中,并在下一帧移除。
v-leave-to:元素离开过渡的结束状态。在离开过渡触发后的下一帧添加,同时 v-leave 被移除。当从 DOM 中移除元素时,该类也会被移除。
在离开阶段,并不会立即从 DOM 中移除元素。当过渡结束后,才会将其移除,这样用户可以看到动画效果。进入 v-enter-active
v-enter v-enter-to
透明度0 -> 透明度1
离开 v-leave-active
v-leave v-leave-to
透明度1 -> 透明度0
<transition>
组件会自动检测应用在元素上的 CSS 过渡效果的持续时间。
复用动画
1 | <transition name="fade"> |
1 | /* transition.css */ |
现在只需要通过 <transition name="fade">
标签就可以在任意元素上复用这个动画了。
贝塞尔曲线缓动函数,可以使得动画更加平滑。
1 | .hand-enter-active .wrapper, |
为元素列表添加动画效果,需要使用另外一个特殊的组件 <transition-group>
。当元素被添加、移除和移动时,该组件将对它的子元素做出动画效果。 跟 <transition>
元素不同的是,<transition-group>
默认情况下会作为<span>
元素出现在 DOM 中。可以使用 tap prop 修改这个 HTML 元素。
1 | <transition-group tag="ul"> |
<transition-group>
的子元素必须由唯一的 key 做标识。
1 | // 指定过渡效果名称为 card |
特殊的 key 属性
当 Vue 更新存在于 v-for 循环中的 DOM 元素列表时,会尽量最小化应用于 DOM 的操作,例如添加和移除元素。大多数情况下,这是更新 DOM 的一种非常高效的方法,并且对性能的提升也有帮助。
为了做到这一点,Vue 会尽可能地复用元素,并仅对 DOM 中需修改的地方进行最小范围的修改,以达到理想的结果。这意味着重复的元素会被打包到一起,不会在添加或移除列表中的项时移动它们。不过,这也意味着对其应用过渡不会有动画效果。
key 属性为元素指定唯一标识符。
使用插槽分发内容
1 | <div class="overlay" @click="handleClick"> |
1 | <overlay> |
Vue 提供了一个特殊的组件可以把其转换为任意的组件:component 组件。只需要将它的 is prop 设置为一个组件名或组件定义对象,甚至是一个 HTML 标签,component 组件就会变为响应的内容:
动态修改组件
1 | <overlay v-if="activeOverlay"> |
当 targetHeight 属性发生改变时,就开始播放动画
1 | watch:{ |
高级项目设置
4.1 设置开发环境
安装 Node.js 和 npm
4.1.1 安装官方命令行工具 vue-cli
安装 vue-cli 并将其作为一个全局的包:
1 | npm install -g vue-cli |
项目脚手架
推荐的官方模板是 webpack 模板,具有使用 Vue 创建整个 SPA(单页面应用)所需的全部功能。使用 webpack-simple 并逐步引入功能。
渲染函数
Vue 使用一个虚拟 DOM 的实现,用树状结构的 JavaScript 对象来构建虚拟 DOM。然后 Vue 将虚拟 DOM 应用到真实浏览器的 DOM 上,所用方法是计算两者之间的差距。这尽可能地避免了 DOM 操作,因为 DOM 操作通常是主要的性能瓶颈。
按照惯例,h 是 createElement 的别名,这是编写 JSX 时非常常见和必须的。它得名于使用 JavaScript 描述 HTML 的技术 - Hyperscript。
createElement(或称 h)方法最多需要 3 个参数,
(1)第一个参数是元素类型。它可以是一个 HTML 标签名称(比如 div),在应用中注册过得组件名称,或者直接就是一个组件定义对象。
(2)第二个参数是可选的。它是一个定义了属性、prop、事件监听器等的数据对象。
(3)第三个参数也是可选的。它可以是简单的纯文本,也可以是一个用 h 创建的其他元素的数据。
示例:
1 | render(h){ |
4.2.4 配置 Babel
Babel 是一个 JavaScript 代码编辑工具,以便我们在旧版和最新版的浏览器中使用新特性(如 JSX 或箭头函数)。建议在所有正式的 JavaScript 项目中使用 Babel。
polyfill 是用于检查特性在浏览器中是否可用的代码;如果不可用,它将实现这个特性,使其可以像原生的一样工作。
1.Babel Vue 预览
安装并使用 babel-preset-vue
1 | npm i -D babel-preset-vue |
主要的 Babel 配置是在项目根目录下已存在的 .babelrc JSON 文件中完成的。
打开这个 .babelrc 文件并将 vue 预览添加到相应的列表中:
1 | { |
polyfill
添加 Babel polyfill,以便在旧浏览器中使用新的 JavaScript 特性。
在开发依赖中安装 babel-polyfill 包。
1 | npm i -D babel-polyfill |
在 src/main.js 文件的开头将其导入
import ‘babel-polyfill’
这将为浏览器启用所有必要的 polyfill。
4.2.5 更新依赖
1、手动更新
1 | // 要检查项目中使用的包是否有新的版本,可以在根文件夹中运行以下命令: |
2.自动更新
1 | npm update |
3.更新 Vue
更新包含核心库的 Vue 包时,你也应该更新 vue-template-compiler 包。它是使用 webpack (或其他构件工具)时编译所有组件模板的包。
这两个包必须始终处于相同的版本。例如,如果你使用 vue 2.5.3 ,那么 vue-template-complier 也应该是版本 2.5.3.4.2.6 为生产而构建
1 | npm run build |
默认情况下,使用 webpack-simple 模板时,它会将 JavaScript 文件输出到项目的 /dist 文件夹中。
4.3 单文件组件
Vue 具有自己的格式,名为单文件组件(SFC)。该格式由 Vue 团队创建,文件扩展名为 .vue。允许每个文件编写一个组件,将模板以及该组件的逻辑和样式集中在一个位置,这里的主要优势在于,每个组件都明显独立,更易于维护,易于共享。
单文件组件使用类似 HTML 的语法描述 Vue 组件。它可以包含 3 种类型的根块
1 | <template>,使用我们已经用过的模板语法描述组件的模板; |
示例:
1 | <template> |
编辑 main.js 文件,并使用 import 关键字导入单文件组件;
1 | import Test from './Test.vue' |
移除 render 选项,使用对象展开运算符复制 Test 组件的定义:
1 | new Vue({ |
4.3.1 模板
标签包含组件的模板。带有 Vue 特殊语法的 HTML。
如果没有在单文件组件中放置一个 标签,则要编写一个渲染函数,否则你的组件将无效。使用 Pug
Pug 是一种编译到 HTML 的语言。可以在 lang 属性设置为 ‘pug’ 的 标签内使用它:
1 | <tempate lang="pug"> |
为了能够编译单文件组件中的 Pug 代码,需要安装这些包:
1 | npm install --save-dev pug pug-loader |
JSX
JSX 是在 JavaScript 代码中用来表示 HTML 标记的特殊符号。它使负责描述视图的代码在更接近纯 HTML 语法的同时,仍具有 JavaScript 的全部功能。
示例:
1 | <script> |
4.3.3 样式
有作用域的样式
可以使用 style 标签的 scoped 属性将标签内的 CSS 作用域限定在当前组件中。这个 CSS 只会应用于这个组件模板里的元素。
示例:
1 | <style scoped> |
这要归功于 PostCSS 应用到模板和 CSS 的一个特殊属性。
示例:
1 | <template> |
等价
1 | <template> |
添加预处理器
Sass
要在组件中启用 Sass,请安装以下包:
1 | npm install --save-dev node-sass sass-loader |
示例:
1 | <style lang="sass" scoped> |
Less
使用 Less,需要安装以下包:
1 | npm install --save-dev less less-loader |
示例:
1 | <style lang="less" scoped> |
Stylus
使用 Stylus ,需要安装这些包:
1 | npm install --save-dev stylus stylus-loader |
示例:
1 | <style lang="stylus" scoped> |
4.3.4 组件内的组件
要在另一个组件中使用组件,我们需要导入它并将它暴露在模板中。
示例:
Movie.vue 组件
1 | <template> |
Movies.vue 组件
1 | <template> |
如果使用 JSX,则不需要 components 选项。这是因为如果以大写字母开头,则可以直接使用组件定义:
1 | import Movies from './Movies.vue' |
项目 3: 支持中心
5.1 通用应用结构
5.1.1 项目设置
(1)用命令生成一个 Vue 项目。
1 | vue init webpack-simple support-center |
(2)安装编译 stylus 代码所需的包
1 | npm install --save-dev stylus stylus-loader |
(3)移除 src 文件夹中的内容,将在其中放置应用的所有源代码。
(4)然后创建一个 main.js 文件,包含创建 Vue 应用所需的代码:
1 | import 'babel-polyfill'; |
可以尝试使用 npm run dev 命令运行应用了。
5.1.2 路由和页面
主要页面:
主页
公共 FAQ 页面
登陆页面
工单页面
发送新工单的页面
显示工单详情和对话的页面
路由是表示应用 state 的路径,通常以页面的形式显示。每个路由都与一个 URL 模式相关联,后者在地址匹配时触发路由。然后,相应的页面将呈现给用户。
Vue 插件
(1)下载 vue-router 包:
1 | npm install --save vue-router |
(2)创建 router.js 文件,并从相应的包中导入 Vue 库 和 VueRouter 插件:
1 | import Vue from 'vue'; |
(3)将该插件安装到 Vue 中:
1 | Vue.use(VueRouter); |
使用 vue-router 创建第一个路由
使用 router-view 进行布局
在添加路由之前,我们需要为应用设置一个布局。这是将要渲染路由组件的地方。
(1)在 src 目录中新建一个 components 文件夹,并在文件夹中创建一个名为 AppLayout.vue 的组件。
(2)编写组件的模板
(3)添加 stylus 文件
1 | <template> |
导入并渲染在 Vue 根实例上:
1 | import router from './router' |
创建路由
创建一个 Home.vue 组件
1 | <template> |
创建一个 FAQ.vue 组件
1 | <template> |
在 router.js 文件中,导入两个组件
1 | import Home from '../components/Home.vue' |
路由模式
可以在构造器选项中使用 mode 参数更改路由器模式,可以是 hash(默认)、history 或 abstract。
hash 模式是默认模式。这是“最安全”的选择,因为它与任何浏览器和服务器都兼容。它使用 URL 的 hash 部分(指 # 符号后面的 部分),并对其进行更改或响应其变化。最大的好处是,改变 hash 部分不会改变应用运行的真实网页(改变真实网页是非常不好的)。显而易见的缺点则是,它迫使我们不那么优雅的 # 符号将 URL 分成两部分。
感谢 HTML5 的 history.pushState API,我们可以摆脱这个 # 符号,并为应用获得一个真实的 URL!我们需要在构造函数中将模式更改为
history:
1 | const router = new VueRouter({ |
两个问题:
浏览器需要支持这个 HTML5 API,这意味着它不能在 Internet Explorer 9 或更低版本上工作(所有其他主流浏览器都已经支持它一段时间了)。
服务器必须配置为当访问诸如 /faq 之类的路由时发送主页而不是抛出 404 错误,因为它并不真实存在(没有名为 faq.html 文件)。这意味着我们不得不自己实现 404 页面。
abstract,可以在任何 JavaScript 环境中使用(包括 Node.js)。如果没有可用的浏览器 API,路由器将被迫使用此模式。
3.创建导航菜单
新建 NavMenu.vue 文件:
1 | <template> |
添加到布局中。在 AppLayout 中导入新组建:
1 | <template> |
路由器链接
vue-router 插件为我们提供了特殊组件 router-link。当这个组件被点击时,就会变为指定路由,这要归功于它的 to prop。默认情况下,它将是一个 HTML a 元素, 但可以使用 tag prop 来自定义。
示例:
1 | <router-link to="/faq">FAQ</router-link> |
这将动态地为路由生成正确的路径。我建议使用第二种方法,而不是只指定路径 —— 这样,这样,如果更改路由的路径,导航链接仍然可以工作。
当使用对象记法时,不要忘记用 v-bind 或者 : 简写来绑定 to prop,否则 router-link 组件会得到一个字符串,并不会理解它是一个对象。active class
路由器链接在与其关联的路由当前处于激活状态时获取 active class。默认情况下,组件使用 router-link-active CSS 类。
默认情况下,active class 匹配行为是包容的!这意味着如果路径为 /fag 或以 /fag/开头,router-link to = ‘/faq’ 都将获得 active class 。
为了防止发生这种情况,有一个 exact prop,它是布尔值。如果设置其为 true,则仅在当前路径完全匹配时,链接才能获得 active class。
示例:
1 | <router-link :to="{ name: 'home' }" exact>Home</router-link> |
5.2.2 使用 fetch
fetch API 是基于 Promise 的,使用起来非常简单。以下是 fetch 用法的示例:
1 | fetch(url).then(response => { |
加载动画
创建全局组件
新建 Loading.vue 文件:
1 | <template> |
新建 global-components.js。使用 Vue.component() 方法全局地注册 Loading 组件;
1 | import Vue from 'vue' |
在 main.js 文件中导入 global-components.js 模块:
1 | import './global-components' |
回到 FAQ.vue 组件,使用动画
1 | <template> |
5.2.3 用自己的插件扩展 Vue
1.创建一个插件
要创建插件,只有一个规则 —— 插件应该是一个带有 install 方法的对象,该方法接收 Vue 构造函数作为第一个参数以及一个可选的 options 参数。该方法将通过修改构造函数为框架添加新特性。
(1)在 src 文件夹中新建 plugins 文件夹。
(2)在 plugins 文件夹中新建 fetch.js 文件。
(3)导入一个带 install 对象的方法,尝试创建插件:
1 | export default { |
(4) 导入插件
1 | import VueFetch from './plugins/fetch' |
2.插件选项
(1)编辑 install 方法
1 | export default { |
(2)添加 baseUrl 属性到配置中:
1 | Vue.use(VueFetch,{ |
(3)将 baseUrl 存储到一个变量中
1 | let baseUrl; |
3.$fetch 方法
(1)使用 fetch 实现 $fetch 方法:
1 | export async function $fetch(url){ |
(2) 为了所有组件中可用,只需要将其添加到 Vue 的原型(这是用于创建组件的构造函数)中即可:
1 | export default{ |
(3)重构 FAQ 组件
1 | this.loading = true; try{ this.questions = await this.$fetch('questions'); } |
使用 mixin 复用代码
mixin 是可应用于其他定义对象(包括其他 mixin)的组件定义对象。
(1)导出一个具有数据属性的定义
1 | export default{ |
(2)使用 mixin
1 | import RemoteData from '../mixins/RemoteData' |
mixin 被应用合并到了 FAQ.vue 组件定义中。意味着 data 钩子被调用两次:首先在 mixin 中调用,然后在 FAQ 定义中添加了一个新属性!
Vue 会自动合并标准选项,如钩子、数据、计算属性、方法和侦听器,如果有一个方法的属性具有相同名称,最后应用的那个将覆盖之前的那些。 data、created、mounted 等钩子都按它们应用于最终定义的顺序逐一调用。 这意味着最后的组件定义钩子将被最后调用。1.获取远程数据
(1)使用一个带有 resources 参数的函数封装的对象:
1 | export default function (resources){ |
(2)改变在 FAQ.vue 组件中使用 mixin 的方式
1 | mixins:[ RemoteData({ questionList: 'questions' }) ] |
(3)数据属性初始化,以便 Vue 设置响应式属性:
1 | data(){ |
(4)创建新的 fetchResource 方法获取资源并更新数据
1 | methods: { async fetchResource (key,url){ try{ this.$data[key] = await |
(5)在 created 钩子中自动调用它
1 | created(){ for(const key in resources){ let url = resources[key] |
(6)更改模板
1 | <article v-for="question of questionList"> |
2.加载管理
(1)增加两个语句,分别递增和递减计数器:
1 | async fetchResource(key, url) { |
(2)添加计算属性
1 | computed: { |
(3)改变 Loading 组件
1 | <Loading v-if="remoteDataBusy" />> |
3.错误管理
(1)错误存储
1 | // 初始化数据属性 |
(2)fetchResource 方法
1 | async fetchResource(key, url) { |
(3)检查
1 | computed: { |
(4)替换 error 属性
1 | <div class="error" v-if="hasRemoteErrors"> |
5.3 支持工单
5.3.1 用户认证
1.将用户存储在一个集中式 state 里
(1)新建 state.js 文件,用于导出 state 对象
1 | export default { |
(2)导入 state:
(3)根实例的数据,以便 Vue 使其成为响应式的:
1 | import state from './state' |
另一个插件
(1)在 plugins 文件夹中,创建新插件 state.js 文件:
1 | export default { |
使用 JavaScript Object.defineProperty()方法在 Vue 原型上设置一个 getter,每个组件都会继承它!
(2)在 main.js 文件中,导入新的插件:
(3)使用 state 对象作为选项参数进行安装:
1 | import VueState from "./plugins/state"; |
2.登录表单
(1)在 components 文件夹中创建一个新的 SmartForm.vue 组件:
1 | <template> |
SmartForm 组件有 3 个属性
title:显示在 h2 元素中。
operation:提交表单时调用的异步函数,它应该返回一个 Promise。
valid:布尔值,以防止表单在无效时调用操作。
1 | <script> |
声明 prop —— 通过使用对象,可以指定 prop 的更多细节。Vue 检查的类型。
busy:布尔值,用于切换加载动画的显示。
error:错误消息,没有则为 null。
(3)添加到 data 钩子里:
1 | data() { return { error: null, busy: false }; } |
(4)编写提交表单
1 | methods: { async submit() { if (this.valid && !this.busy) { this.error = null; |
(5)注册
1 | import SmartForm from './components/SmartForm.vue' |
1 | <style lang='stylus' scoped> |
连结符允许将模板中使用的组件内的元素作为目标,同时限定 CSS 选择器其余部分的作用域。
生成的示例:
1 | .form[data-v-0e596401] .content{ |
不使用连结符,则会
1 | .form .content[data-v-0e596401] { |
带导航守卫的私有路由
路由元属性
1 | {path: '/tickets' , /* ... */ , meta: { private: true }} |
路由器导航守卫
beforeEach
to 是当前的目标路由;
from 是以前的路由;
next 是为了完成解析不得不在某个时刻调用的函数。
(1)在导出路由器实例之前,添加 beforeEach 导航守卫
1 | router.beforeEach((to,from,next)=>{ |
(2)现在需要确定目标路由是否为私有路由:
导航到期望的路由
1 | if(to.meta.private && !state.user){ |
项目 5:在线商店以及扩展
高级开发流程
1 | vue init webpack-simple e-shop |
本地 API
1 | npm i -D json-server |
添加脚本启动 JSON 服务器
1 | "db": "json-server --watch db.json" |
使用 PostCSS 自动添加前缀
vue-loader 已经包含了 PostCSS
1 | npm i D autoprefixer |
在项目根目录添加 postcss.config.js 配置文件。
1 | module.exports = { |
通过 browserslist 指定浏览器
1 | > 1% |
通过 ESLint 提升代码质量和风格
使用 StandardJS 预设规则以及 eslint-plugin-vue
1 | npm i -D eslint eslint-config-standara eslint-plugin-vue@beta |
配置 ESLint
1 | // .eslintrc.js |
Webpack 中使用 ESLint
1 | npm i -D eslint-loader friendly-errors-webpack-plugin |
1 | module: { |
Jest 单元测试
1 | npm i -D jest vue-test-utils vue-jest jest-serializer-vue vue-server-renderer babel-jest babel-plugin-dynamic-import-node |
配置 Jest
在项目根目录中创建 jest.config.js 配置 Jest;
1 | module.exports = { |
1 | // BaseButton.spec.js |
在 package.json 添加运行脚本
1 | "jest": "jest" |
vue-i18n
国际化
1 | npm -i -S vue-i18n |
1 | // src/plugins.js |
项目 6:使用 Meteor 开发实时仪表盘
安装 Meteor 并设置项目
Meteor
Meteor 是一个用于构建 Web 应用的全栈 JavaScript 框架。
Meteor 栈主要由以下元素构成:
- Web 客户端(可以使用任意前端库,如 React 或 Vue),包含一个名为 minimongo 的客户端数据库;
- 基于 Node.js 的服务器,支持现代的 ES2015+ 特性,包括 import / export 语法;
- 服务器使用 MongoDB 实时数据库;
- 客户端和服务器的通信是抽象的,客户端和服务端数据库能方便地实时同步;
- 可选的混合移动应用(Android 和 IOS),能用一条命令构建;
- 完整的开发者工具,如功能强大的命令行实用程序和易用的构建工具;
- Meteor 专用包(也可以实用 npm 包)。
Meteor 没有实用 Webpack。
安装 Meteor
我有单独 Meteor 文章可以那里有安装详细
1 | // 查看当前 Meteor 版本 |
创建项目
1 | meteor create --bare <folder> |
–bare 参数告诉 Meteor 创建一个空的项目。
1 | // 添加 编译 vue 组件,编译 stylus 专用包 |
meteor 会启动一个 HTTP 代理,一个 MonoDB 和 Node.js 服务器。
1 | // main.js |
在 Meteor 应用中,建议在 Meteor.startup 钩子内创建你的 Vue 应用,这样可以保证整个 Meteor 系统在启动前端之前准备完毕。
使用 Meteor 方法将数据保存到 Meteor 集合(collection) 中
将 Meteor 集合集成到应用中,用于应用的自动更新。
1 | meteor npm i -S vue-meteor-tracker |
1 | // main.js |
设置 Meteor 集合,用于存储测量记录数据。
1 | // collections.js |
1 | // methods.js |
Measures 集合发布
1 | // server/publications.js |
相关资料
关于vuex-router-sync的作用,或者可以解决什么问题√
browserslist
jest
Vue 组件单元测试指南
Vue.js 项目实战
install_url
to use ShareThis. Please set it in _config.yml
.