Vue 小结

本篇为 Vue 2.X 的相关内容

class 与 style 绑定变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<!--绑定 class-->
<div :class="{'className' : isShow}">
</div>
<div :class="isShow ? 'className1' : 'className2'">
</div>
<div :class="[class1, class2]">
</div>
<div :class="[{ class1: isShow }, errorClass]">
</div>

<!--绑定 内联样式-->
<div :style="{color: 'red'}">
</div>
<div :style="styleObj">
</div>
<div :style="[styleObj, styleObj2]">
</div>
</template>

<script>
export default {
data () {
return {
isShow: true,
class1: 'className1',
class2: 'className2',
styleObj: {
color: 'red'
},
styleObj2: {
color: 'blue'
}
}
}
}
</script>

prop

需要注意的是,当 prop 属性为 Object, Function 和 Array 类型时,需要使用 return 返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 基本用法
props: ['prop1', 'prop2']

// prop 类型
props: {
prop1: String,
prop2: Object
}

// prop 验证
props: {
prop1: [Number, String],
prop2: {
type: Object,
default: function () {
return {}
}
}
}

computed

计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。

1
2
3
4
5
6
7
8
9
computed: {
aDouble: vm => vm.a * 2,
// 若希望给计算属性进行传参,可以使用如下方式
getValue () {
return function (value) {
return `value:${value}`
}
}
}

methods

注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

路由

路由模式

vue-router 默认使用 hash 模式,通过监听 hashchange 事件来实现局部更新,使用此模式,项目的 url 地址会带有 # 符号,如果不希望使用这种模式的话,可以使用自带的另一种模式即 history 模式:

1
2
3
4
const router = new Router({
mode: 'history',
routes: []
})

history 模式是通过 history.pushState API 来完成 SPA 的,但是需要注意的是:

这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

所以要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

路由传参

除了使用 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
在 Vue 实例内部,你可以通过 $router 访问路由实例,因此你可以调用 this.$router.push。

一个路由对象 (route object) 表示当前激活的路由的状态信息,包含了当前 URL 解析得到的信息,还有 URL 匹配到的路由记录 (route records)。
路由对象是不可变 (immutable) 的,每次成功的导航后都会产生一个新的对象,在组件内部,即 this.$route。

1.params 传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** 传参 **/
//html
<router-link v-for="(item,index) in listData" :to="{name: 'detail',params:{id: item.id}}" >

//js
this.$router.push({
name: 'detail',
params: {
id: id
}
})


/** 取参 **/
//html
<div>
{{ $route.params.id }}
</div>

//js
this.$route.params.id

2.query 传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** 传参 **/
//html
<router-link v-for="(item, index) in listData" tag="div" :to="{path: '/detail', query: {id: item.id}}">{{item.id}}</router-link>

//js
this.$router.push({
path: '/detail',
query: {
id: id
}
})


/** 取参 **/
//html
<div>
{{ $route.query.id }}
</div>

//js
this.$route.query.id

3.路径传参

1
2
3
4
5
6
//配置路由
{
path: '/detail/:id',
name: 'detail',
component: detail
}
1
2
3
4
5
6
7
8
9
10
11
12
/** 传参 **/
//html
<router-link v-for="(item, index) in listData" :to="{path: 'detail/'+ item.id}">{{item.id}}</router-link>

//js
this.$router.push({
path: `/detail/${id}`
})


/** 取参 **/
this.$route.params.id

位置跳转

在列表页跳转到详情页的时候,当列表页发生滚动的时候,跳转的详情页也会相应发生位置的变更,这就很不友好了,可以使用

1
$(window).scrollTop(0);

但其实在 vue-router 中,也已经提供了更详细的 scrollBehavior 方法,在创建 router 实例阶段,添加这个方法:

1
2
3
4
5
6
7
scrollBehavior (to, from , savedPosition){
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}

savedPosition参数当且仅当 popstate(通过浏览器的前进后退按钮触发)时才有用,返回之前的位置 。

vue-router 规定,在使用 scrollBehavior 方法时,必须要在 H5 的 history 模式下才可以使用,然而这就面临一个问题,在服务器端访问的时候,由于从 hash 访问变为 history 模式访问,路由中配置的根路径会直接指向到服务器的根目录,而不是项目本身所在的路径,这个坑又是困扰我了好久,虽然官方文档给出了解决方案,就是修改服务器端的配置文件: 在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

我的服务器是nginx,用了这个方法重启服务器之后,发现问题依然没有解决(具体我也不知道为什么),于是继续向度娘求助,有人说可以直接使用绝对路径,就是在路由配置中将相对路径直接修改为绝对路径,试了这个办法,果然可行,可是这么做本地线上不同步,而且只要是有路由的地方都要修改添加,很耽误开发效率。无意间,看到了一位大神的回答,在路由实例化阶段,有一个 base 属性(官方文档并没有给出),表示应用的基路径,默认为根路径,所以只要修改这个属性值就不再需要进行多余的操作了。

添加返回

使用 History 对象的 history.go(-1),也可以通过 router 的实例方法,使用方法和 window.history 基本一致:

1
2
3
4
5
mechods:{
goBack(){
this.$router.go(-1);
}
}

回到顶部

需要注意的是,若有使用全局监听事件,要在组件销毁之前移除事件,避免影响其它组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<template>
<div class="back-to-top" v-show="backTop">
<a href="#"><i class="fa fa-angle-up"></i></a>
</div>
</template>
<script>
export default {
name: "backToTop"
data(){
return {
backTop: false
}
},
methods: {
handleScroll(){
if(window.scrollY > 500){
this.backTop = true;
} else {
this.backTop = false;
}
}
},
beforeDestory(){
if (this.scrollEvent) {
window.removeEventListener('scroll', this.handleScroll)
}
}
mounted(){
this.scrollEvent = window.addEventListener('scroll', this.handleScroll, false);
}
}
</script>
<style lang="less">
.back-to-top {
position: fixed;
bottom: 10rem;
right: 2rem;
padding: 0 .8rem;
border-radius: .4rem;
background: rgba(0,0,0,0.7);
font-size: 3.2rem;
i{
color: #fff;
}
}
</style>

请求及跨域

axios 基本用法

1.安装

1
2
3
4
5
//npm
npm install --save

//cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2.使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* 使用 params 传参,参数会挂在请求 url 中,使用 data 传参则不会,由于 url 长度限制较小,尽量使用 data 传参 */
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

//多并发请求
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {

}));

//axios(config)
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});

更多使用移步 axios 官方文档

跨域

  1. 服务器不支持跨域请求
    此时可以在 config/index.js 中的proxyTable中添加:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    proxyTable: {
    "/api": {
    target: "http://192.168.1.1:80",
    changeOrigin: true
    pathRewrite: {
    '^/api': ''///api 替代 target
    }
    }
    }
    /*
    * 配置代理之后可以使用 axios 进行跨域请求,但这样做只能在开发模式下使用。
    * 打包部署的时候建议使用 nginx 做代理。
    */
  2. 服务器端支持跨域
    如果能支持跨域那就可以使用 jsonp 来请求了。jsonp 实际上就是通过添加 script 标签来添加的,请求回来的数据也是 js 格式。 axios 不支持跨域,可以手动创建 script 标签,把请求的地址赋给 script 标签的 src 属性,最后添加到 head 标签上,等到请求返回再把标签删掉:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    jsonpRequest: function (a, e) {
    this._ajaxTimer && clearTimeout(this._ajaxTimer);
    this._ajaxTimer = setTimeout(function () {

    var request_script = document.createElement('script');
    request_script.setAttribute("id", "request_script");
    request_script.src = 'http://xxxx'+'&t=' + Date.now();
    window.callback = function (response) {
    // 移除脚本
    document.body.removeChild(document.getElementById('request_script'));
    console.log(response.content);
    }
    document.body.appendChild(request_script);
    }, 500);
    },
  3. 使用 vue-jsonp
    安装

    1
    npm install vue-jsonp --save

引入

1
import jsonp from 'vue-jsonp'

使用

1
2
3
4
5
6
7
this.$jsonp('/api', {
callbackName: 'callback'
}).then(res => {

}).catch(err => {

})
  1. axios-jsonp
    对于使用 axios 进行数据请求的情况,axios 提供了 adapter 配置,允许自定义处理请求
    1
    2
    3
    adapter: function (config) {
    // doSomeThing
    },

安装 axios-jsonp

1
npm install axios-jsonp

使用

1
2
3
4
5
6
7
8
9
10
let axios = require('axios');
let jsonpAdapter = require('axios-jsonp');

axios({
url: '/jsonp',
adapter: jsonpAdapter,
callbackParamName: 'c' // optional, 'callback' by default
}).then((res) => {

});

组件缓存 keep-alive

keep-alive 是 Vue 提供的抽象组件(或称功能型组件),并不会被渲染在 DOM 结构中。它的作用是在内存中缓存组件(不让组件销毁),等到下次再渲染的时候,还会保持其中的所有状态,并且会触发 activated 钩子函数。因为缓存的需要通常出现在页面切换时,所以常与 router-view 一起出现。

1
2
3
<keep-alive :include="['ListView', 'DetailView']">
<router-view />
</keep-alive>

其中,include 属性表示要缓存的组件名(name属性),接收类型有字符串,字符数组以及正则表达式(RegExp)。

除了使用 include 指定缓存的组件之外,有时我们希望从某一个组件跳转到当前组件进行缓存,其它组件跳转当前组件不使用缓存,可以使用导航守卫进行控制。

首先全局定义 keep-alive

1
2
3
4
5
6
7
<!-- 通过每一个路由下 meta 属性决定是否使用组件缓存 -->
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>

在路由中配置是否使用组件缓存

1
2
3
4
5
6
7
8
9
10
routes: [
{
path: '/page',
name: 'page',
meta: {
keepAlive: true
},
component: () => import('page')
}
]

在导航守卫中控制组件缓存与否

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 全局前置守卫进行控制
router.beforeEach((to, from, next) => {
// ...
})

// 组件内的守卫
beforeRouteEnter (to, from, next) {
if (from.name && from.name === 'page') {
to.meta.keepAlive = true
} else {
to.meta.keepAlive = false
}
next()
}

可以看出,后面的用法对于 keep-alive 的配置性来说要更高一些。

Vue.use

安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。

  • 该方法需要在调用 new Vue() 之前被调用。

开启 Vue 调试

对于 vue-cli3,如下方式开启源码调试

1
2
3
configureWebpack: config => {
config.devtool = 'source-map'
}

查看 Vue 版本

1
npm list vue

nextTick 获取不到DOM

nextTick只在当前组件dom更新后触发,并不能监听到子组件渲染完成,可以使用$emit监听或者在mounted中使用setTimeout来解决。

参考文献

  1. vue-router 官方文档
  2. axios
  3. vue-jsonp
作者

晓策

发布于

2017-09-24

更新于

2023-05-25

许可协议

评论