Vue.js项目开发技术解析
一、Vue.js实例
在一个Vue.js工程中,用于显示内容最基层的实例称之为根实例。通过该实例可以进行页面或组件的更新和显示。对于项目本身而言,无论是什么样的页面,都要基于该根实例进行显示。
1.1、何为构造器
对于Vue.js项目来说,每个应用的起步都需要使用Vue.js的构造函数创建一个根实例,如下:
// 逻辑部分代码,建立Vue实例
var vm = new Vue({//选项
})
这里的vm其实是ViewModel的简称。虽然Vue.js并不是完全遵循MVVM模型,但是Vue.js的设计无疑受到了它的启发。
在实例化Vue.js时,需要传入一个选项对象,它包括数据、模板、挂载元素、方法和生命周期钩子等选项,全部的选项可以在API文档中查看。
对于已经创建的相关构造器,可以扩展为其他构造器,相当于对某一构造器的继承,从而达到可复用组件构造器的目的。
演示代码如下:
var MyComponent = Vue.extend({// 扩展选项
})
// 所有的 MyComponent 实例都将以预定义的扩展选项被创建
var myComponentInstance = new MyComponent()
1.2、实例的属性和方法
当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }// 该对象被加入到一个 Vue 实例中
var vm = new Vue({data: data
})// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2// ……反之亦然
data.a = 3
vm.a // => 3
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的属性才是响应式的。也就是说如果你添加一个新的属性,
比如:
vm.b = 'hi'
那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值。
这里唯一的例外是使用 Object.freeze()
,这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
var obj = {foo: 'bar'
}Object.freeze(obj)new Vue({el: '#app',data: obj
})
<div id="app"><p>{{ foo }}</p><!-- 这里的 `foo` 不会更新! --><button v-on:click="foo = 'baz'">Change it</button>
</div>
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。例如:
var data = { a: 1 }
var vm = new Vue({el: '#example',data: data
})vm.$data === data // => true
vm.$el === document.getElementById('example') // => true// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {// 这个回调将在 `vm.a` 改变后调用
})
以后你可以在 API 参考[API 参考]中查阅到完整的实例属性和方法的列表。
1.3、生命周期
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
比如 created
钩子可以用来在一个实例被创建之后执行代码:
new Vue({data: {a: 1},created: function () {// `this` 指向 vm 实例console.log('a is: ' + this.a)}
})
// => "a is: 1"
也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mounted
、updated
和 destroyed
。生命周期钩子的 this
上下文指向调用它的 Vue 实例。
注意:不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch(‘a’, newValue => this.myMethod())。因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。
下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
二、Vue.js路由
Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,让构建单页应用变得易如反掌。
包含的功能有:
- 嵌入的路由/视图表
- 正极的,基于组件的路由配置
- 路由参数,查询,通配符
- 基于Vue.js过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的CSS类的链接
- HTML5历史模式或hash模式,在IE9中自动降级
- 自定义的滚动条行为
两种方式:
1、直接引入
2、npm安装方式
使用Vue.js + Vue Router创建单页应用,是非常简单的。使用Vue.js,我们已经可以通过组合组件来组成应用程序,当你要把Vue Router添加进来,我们需要做的是,将组件(组件)映射到路由(路由),然后告诉Vue Router在哪里渲染它们。下面是一个基本示例:
<!DOCTYPE html>
<html><head><meta charset="utf-8"><script src="https://unpkg.com/vue/dist/vue.js"></script><script src="https://unpkg.com/vue-router/dist/vue-router.js"></script><title></title></head><body><div id="app"><h1>Hello App!</h1><p><!-- 使用 router-link 组件来导航. --><!-- 通过传入 `to` 属性指定链接. --><!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --><router-link to="/foo">Go to Foo</router-link><router-link to="/bar">Go to Bar</router-link></p><!-- 路由出口 --><!-- 路由匹配到的组件将渲染在这里 --><router-view></router-view></div> <script type="text/javascript">// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)// 1. 定义 (路由) 组件。// 可以从其他文件 import 进来const Foo = { template: '<div>foo</div>' }const Bar = { template: '<div>bar</div>' }// 2. 定义路由// 每个路由应该映射一个组件。 其中"component" 可以是// 通过 Vue.extend() 创建的组件构造器,// 或者,只是一个组件配置对象。// 我们晚点再讨论嵌套路由。const routes = [{ path: '/foo', component: Foo },{ path: '/bar', component: Bar }]// 3. 创建 router 实例,然后传 `routes` 配置// 你还可以传别的配置参数, 不过先这么简单着吧。const router = new VueRouter({routes // (缩写) 相当于 routes: routes})// 4. 创建和挂载根实例。// 记得要通过 router 配置参数注入路由,// 从而让整个应用都有路由功能const app = new Vue({router}).$mount('#app')// 现在,应用已经启动了!</script></body>
</html>
通过注入路由器,我们可以在任何组件内部通过this.$router访问路由器,也可以通过this.$route访问当前路由:
// Home.vue
export default {computed: {username() {// 我们很快就会看到 `params` 是什么return this.$route.params.username}},methods: {goBack() {window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')}}
}
上面的示例都常使用router实例。留意一下this.$router和router使用起来完全一样。我们使用this.$router的原因是我们并不想在每个独立需要封装路由的组件中都引入路由。
三、Vue.js路由配置vue-router
3.1、动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
const User = {template: '<div>User</div>'
}const router = new VueRouter({routes: [// 动态路径参数 以冒号开头{ path: '/user/:id', component: User }]
})
现在呢,像 /user/foo 和 /user/bar 都将映射到相同的路由。
一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID:
const User = {template: '<div>User {{ $route.params.id }}</div>'
}
3.2、嵌套路由
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:
3.3、编程式导航
3.4、命名路由
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。
const router = new VueRouter({routes: [{path: '/user/:userId',name: 'user',component: User}]
})
要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
这跟代码调用 router.push() 是一回事:
router.push({ name: 'user', params: { userId: 123 }})
这两种方式都会把路由导航到 /user/123 路径。
3.5、命名视图
3.6、重定向和别名
3.7、路由组件传递参数
3.8、HTML5 History模式
四、数据获取
4.1、导航守卫
4.1.1、全局守卫
4.1.2、全局解析守卫
4.1.3、全局后置钩子
4.1.4、路由独享的守卫
4.1.5、组件内的守卫
4.2、数据获取
4.2.1、导航完成后获取数据
4.2.2、在导航完成前获取数据
…详细的在官网上都有相应的描述。