编写轮子的过程中遇到的问题与总结,
轮子项目地址:https://github.com/o0Chivas0o/my-wheel

—2019-04-09—

  1. Vue组件中Props的变量接受类型可以是指定单个或多个类型.props具体属性可查看vue官方文档:https://cn.vuejs.org/v2/guide/components-props.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //  props有2种写法:
    1.
    props:{
    xxx:{
    type:[String,Number]
    }
    }
    2.
    props:['xxx']
  2. 组件中name属性建议规范命名,便于调试
    1
    //通过 this.$option.name 可以获取到当前组件的名字

—2019-06-17—

  1. props中的default的值如果是对象或者数组,需要用函数return一个对象或数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    props:{
    object:{
    type:Object,
    default:() => {
    return {
    a:'1',
    b:'2'
    }
    }
    },
    array:{
    type:Array,
    default:() => {
    return [
    'a','b','c
    ]
    }
    }
    }


    ### ---2019-07-02---
  • vue 默认合并 :style 和 class里的东西

    1
    2
    <w-tabs class='lee'></w-tabs>
    // html 中 class 为 class='tabs lee'
  • .sync的用法

    1
    2
    3
    <w-tabs :selected.sync="selectedTab">
    <w-tabs selected="selectedTab" @update:selected="selectedTab = $event">
    // 两种写法是等价的
  • vue 的事件不会冒泡

  • css中margin-left:auto可将元素放置在最右

  • porps 与 data的区别

    1
    2
    3
    4
    5
    6
    7
    prop:需要用户传值
    data:不需要用户传值

    function fn(prop1,prop2){
    var data1 = prop1
    data1 = 2
    }
  • 通过provideinject API完成eventBus

    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
    //top-level
    import Vue from 'vue'
    export default{
    ...
    data(){
    return {
    eventBus:new Vue(),
    yyy:false
    }
    },
    provide:{
    eventBus:this.eventBus
    },
    mounted(){
    this.eventBus.$emit('update:xxx',this.yyy)
    }
    }

    //rest of level
    export default{
    ...
    inject:['eventBus'],
    created(){
    this.eventBus.$on('update:xxx',()=>{
    // 通过回调来改变值或进行其他操作
    })
    }
    }

  • 组件生成过程中使用mounted钩子函数进行后续操作,因为created并不能确定组件是否完全生成



    ### ---2019-07-03---
  • this.$children的操作,只能获取到组件内的 子组件 ,并不能获取到组件内的 子元素



    ### ---2019-07-04---
  • 编写popover组件的思路,

    • 点击button使其展示或关闭,此方法仅能通过按钮来展示或关闭,满足不了更多的需求且有bug.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <template>
      <div class="popover" @click="xxx">
      <div class='content-wrapper' v-if="visible">
      <slot name='content'></slot> // 等价展示内容
      </div>
      <slot><slot> // 等价按钮
      </div>
      </template>

      // 靠变量visible使其关闭或显示
      <script>
      ...
      data(){
      return {
      visible:false
      }
      },
      methods:{
      xxx(){
      this.visible = !this.visible
      }
      }
      ...
      </script>
    • 使其点击 body元素 就能关闭popove
      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
      // 接上段代码,更改methods中的xxx函数
      xxx(){
      this.visible = !this.visible
      if(this.visible){
      document.addEventListener('click',()=>{
      this.visible = false
      })
      }
      }

      // 然而以上代码会产生几个bug
      // 1. 监听器并没有删除,点击一次监听器在增加一个
      // 2. 监听是同时进行的,当visible变为ture的瞬间,监听器便将其值更改为false,所以页面中看不到visible
      // 3. 需要使用this.$nextTick或者setTimeout 来使其是异步操作


      // 完善以上代码
      xxx(){
      this.visible = !this.visible
      if(this.visible){
      // 这个操作是因为
      // 1. 箭头函数不具名
      // 2. 如果修改为function x(){}.bind(this) 会新生成另一个函数,与x函数不同,所以提出来这样写
      let x = () => {
      this.visible = false
      document.removeEventListener('click',x)
      }
      this.$nextTick(()=>{
      document.addEventListener('click',x)
      })
      }
      }

      // 此时点击外层的document元素也能使其关闭内容展示
    • 但是用户如果需要复制popover弹出层的内容,而这段代码不能满足该需求,所以修改如下结构.
      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
      <template>
      // vue中给@click事件添加修饰符.stop便阻止冒泡,仅添加这一项便能满足如下需求
      <div class="popover" @click.stop="xxx">
      <div class='content-wrapper' v-if="visible" @click.stop>
      <slot name='content'></slot> // 等价展示内容
      </div>
      <slot><slot> // 等价按钮
      </div>
      </template>

      // 靠变量visible使其关闭或显示
      <script>
      ...
      data(){
      return {
      visible:false
      }
      },
      methods:{
      xxx(){
      this.visible = !this.visible
      let x = () => {
      this.visible = false
      document.removeEventListener('click',x)
      }
      this.$nextTick(()=>{
      document.addEventListener('click',x)
      })
      }
      }
      ...
      </script>

—2019-07-08—

  • popover组件,还需要注意几点.
    • 点击按钮开启,点击其他位置只关闭一次.(事件监听)
    • popover显示内容,不能被具有overflow:hidden属性的元素遮挡,解决办法很简单,通过$refs.popoverContent找到popover显示内容,并通过document.body.appendChild(popoverContent)插入到body中.
    • 在寻找popover的位置时,如果页面出现滚动条,还会出现位置不正确的bug,需要在定位的同时,加上scroll的距离,例如:this.$refs.contentWrapper.style.left = left + window.scrollX + 'px',该情况下是x方向出现滚动条的定位.

### ---2019-07-09--- - 在编写某些标签上的自定义属性,如果用到拼接字符串,需用双引号包起来,如:
1
2
<use :xlink:href="`#i-${name}`"></use>
// 在页面渲染时,通常都需要一些工具或者api进行转义,通常是用正则来匹配引号中的东西去渲染,如果不这样写会造成转义的问题

—2019-07-15—

  • 递归组件的几种写法.
    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
      //以cascader组件为例子

    // 1. 以组件中name为标识,通过组件name递归引用自身
    <template>
    <div class="cascader-item">
    {{sourceItem.name}}
    <w-cascader-item
    v-if="sourceItem.children"
    v-for="item in sourceItem.children"
    :sourceItem="item"></w-cascader-item>
    </div>
    </template>

    export default {
    name: 'WCascaderItem',
    props: {
    sourceItem: {type: Object}
    }
    }

    // 2. 以下形式
    <template>
    <div class="cascader-item">
    {{sourceItem.name}}
    <w-cascader-item
    v-if="sourceItem.children"
    v-for="item in sourceItem.children"
    :sourceItem="item"></w-cascader-item>
    </div>
    </template>

    const WCascadearItem = {
    name: 'WCascaderItem',
    components:{WCascadearItem},
    props: {sourceItem: {type: Object}},
    }

    export default WCascadearItem


    // 上述两种形式都需要在父组件中 import 该组件, 并添加components属性使用该组件.

  • scss @mixinplaceholder 的区别
    • @mixin 会将属性重复写在被选择器选中的元素内(重复写属性)
    • placeholder 会将选择器提取出来写在属性前(不会重复)

—2019-07-17—

  • 编写组件需要用到数组记录的过程当中需注意:
    • vue是不允许直接修改数组的,需要使用this.$set(target,index,value)这样的api来动态修改数组内的值
    • 或者使用this.target.push()或者this.target.splice()等数组方法来间接修改数组内的值.
    • 切记勿使用this.array = value来修改值
    • 参考-深入响应式原理
  • vue编写组件,不允许 子组件 直接 修改props的值
  • 组件的编写尽量单项数据流的思路去做,省去很多麻烦

—2019-07-19—

  • Vue自定义指令
    • 组件如果需要操作dom,尽量封装到Vue指令中,v-xxx这种
    • 具体编写查看vue文档,下面是cascader轮子中用到的自定义指令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// click-outside.js
let onClickDocument = (e) => {
let {target} = e
callbacks.forEach((item) => {
if (target === item.el || item.el.contains(target)) {return}
item.callback()
})
}

// cascader.vue
<template>
<div class="cascader" ref="cascader" v-click-outside="close">
</template>

import ClickOutside from './click-outside'
export defalut {
...
directives: {ClickOutside},
...
}

—2019-07-22—

  • vue动画
    • transition 动画
    • CSS animation 动画 (可搭配animate.css使用)
    • JS 操作动画 (volocity.js 库使用 2.0.0版本以上可能存在问题)
    • 条件渲染时,在渲染元素需要加上key,transition标签加mode

—2019-08-08—

  • vue中 $children 如果是通过 slot 这种方式渲染的 不会马上拿到

—2019-08-10—

  • 移动端 获取用户手指坐标(touchstart事件),通过e.touches[0]来获取第一个手指的坐标

—2019-08-12—

  • vue.config.js 文件中 如若有修改路径目录的东西,需按以下写
1
2
3
4
5
6
7
8
9
const path = require('path')            
module.export = {
css:{
includePath:[path.join(__dirname,'src')]
}
}
__dirname 是当前项目的目录
src 是引用文件所在目录
不用`${__dirname}/src`写法,是因为windows下,路径可能是 '//' 这种

—2019-08-20—

v-if 和 v-show的区别

  1. v-if让元素不出现在页面,而v-show则元素一直在页面.
  2. v-if 为 true 时 元素 create 当 v-if 为false 时 元素 destory,而v-show 只改变style,并没有生命周期

—2019-08-22—

浏览器会合并css属性,例如:

1
2
3
4
5
el.style.height = 0 
el.style.height = `120px`
这样浏览器会认为你要的是120px的高度,在vue中使用动画钩子函数,会使动画失效
此时加上 el.getBoundingClientRect()去强制计算高度
这样动画就不会失效

—2019-08-26—

表单验证有两种,及时验证,异步验证,参考validate.js

  1. 及时验证
    1. 邮箱正则 /^.+@.+$/
    2. 手机 /\d{10}/\d{11}
  2. 异步验证
    1. 需要用到查询数据的

一个关于引用类型的知识点 具体参考:https://segmentfault.com/a/1190000006769676

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
let a = {}
ensureObject(a.x)
a.x // undefined
ensureObject(a,x)
a.x // {}

用var操作符定义的变量将成为作用域中的局部变量。就是说函数中 使用var定义一个变量,这个变量在函数退出后就会被销毁。

基本类型:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
引用类型:存储在堆中的对象,也就是说,存储在变量处的值是一个指针,指向堆中的对象。


// 错误的
function ensureObject (obj) {
if (typeof obj !== 'object') {
obj = {}
}
}

// 正确的
function ensureObject (obj, key) {
if (typeof obj[key] !== 'object') {
obj[key] = {}
}
}

—2019-09-17—

uploader 组件 涉及到简单的node.js构建的静态服务器,其中有一些需要注意的地方

—2020-07-20—

发现table组件 并不能在 column 中 添加标签 如 a 标签进行其他操作,于是修改结构,将table自身column 属性,更改为 table-column 组件 , 通过 slot 来构建table 的 column , 其中发现需要声明一个组件来完成该操作.

  1. 需要构建一个组件来渲染子组件中的html标签
  2. 外层引用 需要用到 slot-scope 的属性
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
<template>
<template v-if="column.render">
<vnodes :vnodes="column.render({value: item[column.field]})"></vnodes>
</template>
<template v-else>
{{item[column.field]}}
</template>
</template>


...
export default {
components: {
WIcon,
vnodes: {
functional: true,
render: (h, context) => context.props.vnodes
}
},
...
mounted:(){
this.columns = this.$slots.default.map(node => {
let {text, field, width} = node.componentOptions.propsData
let render = node.data.scopedSlots && node.data.scopedSlots.default
return {text, field, width, render}
})
}
}

// 外层引用
<template>
<div>
<w-table :data-source="dataSource" bordered :orderBy.sync="orderBy" @update:orderBy="order"
expend-field="description" :height="200" :selected-items.sync="selected" :loading="loading">
<w-table-column text="姓名" field="name" :width="100">
<template slot-scope="props">
<--! 通过slot-scope,可以得到传递给table-column的属性的值 再进行操作即可-->
<a href="#">{{props.value}}</a>
</template>
</w-table-column>
</w-table>
</div>
<template/>

__END__

o0Chivas0o
文章作者:o0Chivas0o
文章出处Vue的一些东西
作者签名:Rich ? DoSomethingLike() : DoSomethingNeed()
版权声明:文章除特别声明外,均采用 BY-NC-SA 许可协议,转载请注明出处