现代化前端框架之Vue

Vue语法相关问题

记录Vue的特性

列表渲染

v-for用于组件时,key必须要传

有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()

1
2
3
4
this.userProfile = Object.assign({}, this.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})

v-for优先级高于v-if,所以两个一起使用的时候,可以按需渲染

条件渲染

和v-for一样只能通过指令的形式写在dom元素或组件中(自定义组件或内置组件)

过滤器

这个很关键,敲黑板:只能用于插值和v-bind中!!

坑位:过滤器的实现,后面再添加

事件

组件上一般会绑定自己抛出的事件,如果想绑定原生事件,需要通过.native修饰符

可以使用其他修饰符,比如.enter,.keyup减少代码量

修饰符.sync是一个语法糖,在父组件中不需要通过v-on来监听事件,在子组件内改变了值之后还是需要显式地抛出事件,

1
2
3
<comp :foo.sync="bar"></comp> // 父组件调子组件
this.$emit('update:foo', newValue) // 子组件抛出事件

使用一些组件绑定事件的时候,如果本身的事件不行,就加.native修饰符

表单

  • 输入框:绑定输入的值
  • 单选:绑定单选的value值
  • 复选框:单个为逻辑值,多个为value组成的数组
  • 下拉框:绑定option的value,如果option没有value,则是option的text
  • 表单修饰符:.number,lazy,trim

    1
    2
    3
    <input v-model.trim="msg"> // 去掉空格
    <input v-model.number="msg"> // 转为小数
    <input v-model.lazy=""> // input触发改为change触发
  • 组件实现v-model,也是语法糖,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <comp v-model="something"></comp>
    {
    ...
    prop: ['value'],
    methods: {
    change (val) {
    this.$emit('input', val)
    }
    }
    ...
    }
  • 对于组件内有单选框、复选框的情况,如果按这种写法来写,需要加model属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    ...
    model: {
    prop: 'checked',
    event: 'change'
    },
    props: {
    checked: Boolean,
    value: String
    }
    }

数据

用到的数据必须在data中先声明,如果不知道初始值,置空或者null

组件

组件写法

slot是在子组件留的坑位,在父组件可以根据需求灵活地传入,包括具名插槽和作用域插槽

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
// 子组件留slot坑位
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
// 父组件调用给子组件分发内容
<app-layout>
<h1 slot="header">这里可能是一个页面标题</h1>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
<p slot="footer">这里有一些联系信息</p>
</app-layout>
// 作用域插槽和具名插槽区别是一个是name,一个是text
<ul>
<slot name="item"
v-for="item in items"
:text="item.text">
<!-- 这里写入备用内容 -->
</slot>
</ul>
<my-awesome-list :items="items">
<!-- 作用域插槽也可以是具名的 -->
<li
slot="item"
slot-scope="props"
class="my-fancy-item">
{{ props.text }}
</li>
</my-awesome-list>

vue用到其他自定义组件要在components属性中定义,从代码可读性方面,让父组件知道引用了哪些子组件

要用到父组件传给子组件的数据,在子组件中定义一个局部变量然后用props初始化,主要是为了子组件改父组件传进来的值。props有以下几种写法

1
2
3
4
5
6
7
8
9
10
props: ['value']
props: {
value: 'String'
}
props: {
value: {
type: 'String',
default: 'hello world'
}
}

组件调用

三种调用方式

  • js代码,将组件封装成一个方法直接调用
  • Vue指令,通过Vue注册插件的形式
  • 通过内嵌组件的显示隐藏

组件传值

组件属性接受横杠式写法,但是组件内部接受到的变量需要转成驼峰写法

子组件需要父组件异步数据初始化组件的时候,先传进来,然后赋值给对应的值,并且watch值,防止异步数据返回得很慢,并且当数据更新的时候,更新组件。这么做的原因是数据还没来,组件已经渲染成功。这种问题其实可以通过等到父组件数据到来时候再渲染子组件,Vue不建议直接通过new组件的形式(或者我不知道这种形式的写法),通过内嵌组件的写法,我们需要通过v-if,v-for来控制组件的创建和销毁

组件封装方法总结

单页面的形式引入组件的三种方式

  • 通过内嵌组件的方式,通过暴露在全局中或者import局部引入的组件内嵌在页面
  • 通过封装一个组件然后注册一个方法,通过传参的形式调用这个组件(参考element-ui MessageBox的实现)
  • 通过 new import进来的组件传入相应的参数来调用

进阶

自定义指令,自定义过滤器,写计算属性,混入选项,插件的写法,封装组件(slot, props, event)

什么时候要写插件?插件有一个install方法

  • 添加全局方法或者属性,如: vue-custom-element
  • 添加全局资源,指令/过滤器/过渡等,如 vue-touch
  • 通过全局 mixin 方法添加一些组件选项,如: vue-router
  • 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  • 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如 vue-router

可复用组件想不重复使用,使用v-if, v-for, :key

可复用路由想不重复使用,使用keep-alive的exclude属性,匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。

ref绑定在普通元素上,拿到的是 dom,绑定在组件上,拿到的是组件的 实例,可以直接调用组件的方法和属性

  • 组件中状态控制变量过多,需要大量定义这种变量使得页面很冗余
  • keep-alive的组件,生命周期只走一遍,可以使用activated钩子函数。所以如果vue-router传值,直接定义在data中的话,param的值将会被缓存,必须在activated钩子函数中取值赋值
  • 数组通过arr[index] = newItem赋值将不会更新,通过变异方法或者this.$set来实现

vue-router遇到的问题

vue-router传值

对于路由路径没有定义为‘xx/:id’的路由,编程式导航,push一个params的时候,params刷新后将拿不到,如果定义为’xx/:id’的形式,则在路由组件中可以直接拿到params的值,要想刷新后拿到参数,push 的时候用query传值,但对于keep-alive组件,必须在activated钩子函数中获取param的值,如果直接写在data中,值将会被缓存

vue-router获取值

  1. this.$route.params.xx
  2. this.$route.query.xx
  3. 路由导航事件 beforeRouteUpdate(也是/:id的形式才有这个事件)
  4. watch $route(也是/:id的形式才能watch到)
  5. vuex(刷新后存不住数据)
  6. sessionStorage,localStorage
  7. cookie

vue-router导航守卫

  1. 路由独享route中可以配置beforeEnter,有三个参数,可以在当前路由中设置在进入前的一些操作
  2. router全局方法.beforeEach, afterEach,全局路由守卫,在进入每一个路由的时候都会判读
  3. 组件内导航钩子,beforeRouteEnter, beforeRouteUpdate, beforeRouteLeave
  4. 其他导航守卫https://router.vuejs.org/zh-cn/advanced/navigation-guards.html
    beforeRouteUpdate只是在可复用组件之间跳转的时候才会触发,比如/foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,

vue-router动态传入路由

通过this.$router.addRoutes来添加,参数为数组,和配置中的route一样,但是有一个大bug是刷新后,路由配置又回到初始路由了,所以需要在页面入口(App.vue)处再增加路由的添加。

vue-router其他

命名视图是指平铺多个视图,而不是嵌套

结合element-ui需要考虑的业务交互问题

  • 输入框:autoComplete(模糊匹配),remote search(常用)
  • 下拉框:过滤,可清除,下拉加载更多,远程加载数据
  • 日期选择:开始时间和结束时间的相互关联。组件中监听事件不能缩写,用v-on
  • 文案:文案需要在前期考虑清楚
  • loading
    • 涉及要异步加载的button要加loading,element-ui中通过v-bind:loading
    • 在异步数据回来之前table要加loading,指令为v-loading。如果路由的全局导航守卫中有设置全局的loading,则对于keep-alive状态下的组件,在activated生命周期下需要设置为false
  • element-ui中的Pagination如果不传page_size,有默认值,为10

函数传参以及获取后端返回的值需要考虑的问题

  • 输入数据的地方考虑数据格式
  • 拿到数据的时候,比如函数入参,是需要判断值否有效。用数据适配器处理
  • 给后端传参的时候,组件中用到的数据和最后要传的值可以不同,因为组件一般的值有封装好的格式,最后传值可能不是这种格式,需要一个数据适配器来处理一下。