vue学习纪录(一)

vue 插件

四种类型

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

个人练习的是实现类似Toast/Loading的插件,实现方法是上述第四个,即添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现

组件和插件的关系

  • 组件是需要的地方引入,插件可以全局使用
  • 插件可以封装组件,组件可以暴露数据给插件

实现步骤
核心思想 实例化组件,并挂载组件;在vue原型链上添加方法操作上面的vue实例

  • 建立一个vue文件,实现一个组件
  • 建立一个js文件,封装插件,
  • 在全局引入
  • 在需要的地方调用

toast.vue

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<template>
<div class="modal" v-show="isShow">
<div :class="level">
{{text}}
</div>
</div>
</template>

<script>
const levels = ['success','warm', 'info']
export default {
computed:{
level(){
if (levels.indexOf(this.type) != -1){
return this.type
}else{
return ''
}
}
},
data(){
return {
isShow:false
}
},
props:{
type:{
type:String,
default: 'success'
},
duration:{
type: Number,
default: 3000
},
text:{
type:String,
default:'你忘记传入值进来啦'
}
},
methods:{
show(){
this.isShow = true
},
disappear(){
this.isShow = false
}
}
}
</script>

<style scoped>
.modal{
position: fixed;
width: 100%;
height: 100%;
background:rgba(0,0,0,0.5);
left: 0;
top:0;
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
}
.message{
background-color: #fff;
opacity: 1;
width: 300px;
height: 70px;
}
.warm{
border: 1px solid red;
}
.success{
border: 1px solid green;
}
.info{
border:1px solid blue
}
</style>

config.js

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
import toast from './toast'

const ToastPlugin= {}

ToastPlugin.install = function(Vue, options){
// 实例化组件
const toastComp = Vue.extend(toast)
let toastInstance


const initInstance = function(){
toastInstance = new toastComp()
// 挂载组件
var html = toastInstance.$mount().$el
document.body.appendChild(html)
}



// 在vue原型链上添加方法操作上面的vue实例
Vue.prototype.$toast ={
show:function(options){
if(!toastInstance){
initInstance()
}

Object.assign(toastInstance,options)
toastInstance.show();

setTimeout(()=>{
toastInstance.disappear()
},toastInstance.duration)
}
}
}

export default ToastPlugin

main.js

1
2
import Toast from './plugin/toast/config'
Vue.use(Toast)

全局使用

1
this.$toast.show({type:'success', duration:3000, text:'hello'})


vuex demo

一个小demo, 示范Vuex 中getter/mutaions/actions的一般用法

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuex demo</title>
</head>
<body>
<div id="app">
<btn></btn>
</div>

<script src="https://cdn.bootcss.com/vue/2.3.0/vue.js"></script>
<script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.js"></script>
<script>
getData = ()=>{
return new Promise((resolve,reject)=>{resolve(100)})
},
getOtherData = ()=>{
return new Promise((resolve,reject)=>{resolve(200)})
}

const store = new Vuex.Store({
state: {
count: 0,
todos: [
{ id: 1, text: 'A', done: true },
{ id: 2, text: 'B', done: false },
{ id: 3, text: 'C', done: true },
{ id: 4, text: 'D', done: false }
]
},

getters:{
doneTodos: state=> {return state.todos.filter(todo => todo.done)}
},

mutations: {
increment (state,payload) {
if(payload.offset)state.count += payload.offset
else state.count += 1
},
gotData(state, payload){
console.log('mutation: gotData', payload)
return new Promise((resolve,reject)=>{resolve(300)})
},
gotOtherData(state, payload){
console.log('mutation: gotOtherData', payload)
return new Promise((resolve,reject)=>{resolve(400)})
}
},

actions: {
increment ({commit},payload) {
commit('increment',payload)
},
async actionA({commit, state}){
let data = {...state}
console.log(data)
commit('gotData',await getData())
},
async actionB({dispatch,commit}){
await dispatch('actionA'); // action 一般是异步操作,触发后返回的是promise
commit('gotOtherData', await getOtherData())
}
}
})
</script>

<script>
let {mapState, mapMutations, mapActions, mapGetters} = Vuex

Vue.component('btn', {
template:
`<div><button @click="addNum({offset:1})">Add 1</button>
</br>
<button @click="add3({offset:3})">Add 3</button>
<p>data is :{{count}}</p>
<button @click="trigger">test</button>
<div v-for="(item, index) in doneTodos">{{item.text}}</div>
</div>`
,
computed:{
...mapState(['count']) ,
...mapGetters(['doneTodos'])
},
mounted(){
console.log(this.$store)
},
methods:{
...mapMutations({'addNum':'increment'}),
...mapActions({'add3':'increment'}) ,
...mapActions({'trigger':'actionB'})
}
})

new Vue({
el:"#app",
store
})
</script>
</body>
</html>


vue项目目录结构

  • assets
  • config
    • env.js(dev和production环境配置切换)
    • utils.js (基础js方法库)
    • fetch.js (对axois库进行封装,统一处理请求错误)
  • components (组件)
  • filter
    • index.js (过滤器,需在全局注册)
    • site.js (不同过滤内容)
    • taskStatus.js
  • page (页面)
    • base.vue (嵌套路由的基础页面)
  • plugin
  • router (路由配置)
  • service
    • index.js (网络服务层)
    • roleA.js (不同权限或角色的请求)
    • roleB.js
  • store
    • index.js (全局状态)
    • mutation-type.js

全局scss文件

  • style
    • mixin.scss
    • common.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@mixin wh($width, $height){
width:$width;
height:$height
}
@mixin center(){
position:absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%)
}
@mixin fj($type:space-between){
display:flex;
justify-content: $type
}
1
2
3
4
5
6
7
8
9
10
11
.ellipsis{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.multiple3{
display: -webkit-box;
overflow: hidden;
word-break:break-all;
-webkit-line-clamp:3;
}

自定义vue双向绑定

v-model作用在标签上

1
<input v-model="price">

语法糖,实际上是

1
<input :value="price" @input="price=$event.target.value">

v-model作用在自定义组件上

1
<cust-input v-model="price"></cust-input>

语法糖,实际上是

1
2
<cust-input :value="price" @input="price=argument[0]">
<cust-input :value="price" @input="price= $event"">

要让组件的v-model生效,必须

  • 接受一个value属性
  • 在新value的时候触发input事件。$emit
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue双向绑定</title>
</head>
<body>

<div id="demo">
<currency-input v-model="price"></currency-input>
<!-- <currency-input :value="price" @input="price = arguments[0];this.console.log('childTemplate')"></currency-input> -->

<span>{{price}}</span>
<base-checkbox v-bind:checked="checked" v-on:change="checked=arguments[0]"></base-checkbox>

<div>
<input type="text" v-model="num"><input type="text" :value="num" @input="num=$event.target.value">
{{num}}
</div>
</div>

<script src="https://cdn.bootcss.com/vue/2.3.0/vue.js"></script>
<script>

Vue.component('currency-input', {
template:
`<span><input ref="input" :value="value" @input="$emit('input',$event.target.value);this.console.log('childComp')"></span>`
,
props: ['value'],
})

//一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的。model 选项可以用来避免这样的冲突
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:click="$emit('change', $event.target.checked)"
>
`
})

var demo = new Vue({
el: '#demo',
data: {
num:50,
price: 100,
checked:true
}
})
</script>
</body>
</html>