vue小结

1.vue常用对象

var vm = new Vue({
// 数据
    data: "声明需要响应式绑定的数据对象",
    props: "接收来自父组件的数据",
    propsData: "创建实例时手动传递props,方便测试props",
    computed: "计算属性",
    methods: "定义可以通过vm对象访问的方法", 
    watch: "Vue实例化时会调用$watch()方法遍历watch对象的每个属性",
// DOM
    el: "将页面上已存在的DOM元素作为Vue实例的挂载目标",
    template: "可以替换挂载元素的字符串模板",
    render: "渲染函数,字符串模板的替代方案",
    renderError: "仅用于开发环境,在render()出现错误时,提供另外的渲染输出",
// 生命周期钩子
    beforeCreate: "发生在Vue实例初始化之后,data observer和event/watcher事件被配置之前",
    created: "发生在Vue实例初始化以及data observer和event/watcher事件被配置之后",
    beforeMount: "挂载开始之前被调用,此时render()首次被调用",
    mounted: "el被新建的vm.$el替换,并挂载到实例上之后调用",
    beforeUpdate: "数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前",
    updated: "数据更改导致虚拟DOM重新渲染和打补丁之后被调用",
    activated: "keep-alive组件激活时调用",
    deactivated: "keep-alive组件停用时调用",
    beforeDestroy: "实例销毁之前调用,Vue实例依然可用",
    destroyed: "Vue实例销毁后调用,事件监听和子实例全部被移除,释放系统资源",
// 资源
    directives: "包含Vue实例可用指令的哈希表",
    filters: "包含Vue实例可用过滤器的哈希表",
    components: "包含Vue实例可用组件的哈希表",
// 组合
    parent: "指定当前实例的父实例,子实例用this.$parent访问父实例,父实例通过$children数组访问子实例",
    mixins: "将属性混入Vue实例对象,并在Vue自身实例对象的属性被调用之前得到执行",
    extends: "用于声明继承另一个组件,从而无需使用Vue.extend,便于扩展单文件组件",
    provide&inject: "2个属性需要一起使用,用来向所有子组件注入依赖,类似于React的Context",
// 其它
    name: "允许组件递归调用自身,便于调试时显示更加友好的警告信息",
    delimiters: "改变模板字符串的风格,默认为{{}}",
    functional: "让组件无状态(没有data)和无实例(没有this上下文)",
    model: "允许自定义组件使用v-model时定制prop和event",
    inheritAttrs: "默认情况下,父作用域的非props属性绑定会应用在子组件的根元素上。当编写嵌套有其它组件或元素的组件时,可以将该属性设置为false关闭这些默认行为",
    comments: "设为true时会保留并且渲染模板中的HTML注释"
});

2. Vue中watch的用法

(1)监听
          route使用场景:当两个路由指向同一个组件是,修改路由视图并未更新,第二个组件的created未执行,这时需要监听route使用场景:当两个路由指向同一个组件时,修改路由视图并未更新,第二个组件的created未执行,这时需要监听route使用场景:当两个路由指向同一个组件是,修改路由视图并未更新,第二个组件的created未执行,这时需要监听route

created () { 
    console.log(this.getStatus(this.$route.path)) 
}, 
methods: { 
    getStatus (urlStr) { 
        var urlStrArr = urlStr.split('/') 
        return urlStrArr[urlStrArr.length - 1] 
    } 
}, 
watch: { 
    '$route' (to, from) { 
        console.log(this.getStatus(this.$route.path)) 
    } 
}

(2) watch默认在数据第一次修改时开始监听,对对象的内部的属性监听不到,只能监听对象的引用。
使用场景:1.组件要求第一次修改之前就开始监听 2. 深度去监听对象的属性。

data() {
    return {
        fullName: '',
        firstName: '',
        lastName: '',
        obj: {
            name: 'huoalong'    
        }
    }
},
watch: { 
    firstName: { 
        handler(newName, oldName) { 
            this.fullName = newName + this.lastName;
        },
        immediate: true    //在第一次修改之前开始监听,
        deep: true,   //深度监听对象内部的属性
    } 
}

deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler,如果我们明确要观察哪个属性时可以这样做

watch: { 
    ‘obj.name’: { 
        handler(newName, oldName) { 
            this.fullName = newName + this.lastName;
        },
        immediate: true    //在第一次修改之前开始监听
    } 
}

(3) 通过组件的属性props设置一个动态变化的值时需要用到watch
场景:父子组件通过props传动态值时,需要在子组件data里面重新定义一个变量对象来盛放props,然后用watch监听。

父组件

<el-dialog title="消息列表" :visible.sync="dialogTableVisible" size="tiny">
        <MessageList :tabs="tabs"></MessageList> 
</el-dialog>

子组件

  props: {
         tabs: {
             type: String,
         }
     },
     data() {
         return {
             //tabs: '课程报名申请',
             activeName2: 'first',
             tab: {
                 tabs: this.tabs,
             }
         };
     },
    watch: {
        tabs: function(value) {
            this.tab['tabs'] = value;
        }
    },

3. 在钩子函数里面用$refs定位DOM时遇到的坑

    在mounted函数里面用$refs定位dom一般会得到undefined的情况,mounted阶段,DOM结构准备就绪,但是这里的准备就绪需要特别说明一下:

    DOM结构已经出来了,但是如果在DOM结构中的某个DOM节点使用了v-if、v-show或者v-for(即根据获得的后台数据来动态操作DOM,即响应式),那么这些DOM是不会再mounted阶段找到的。
   
    此时的mounted阶段,一般是用于发起后端请求,拿回数据,配合路由钩子做一些事情,简单来说就是在mounted钩子中加载数据而已,加载回来的数据是不会再这个阶段更新的DOM中的

    所以如果在mounted钩子中使用$refs,如果ref是定位在有v-if、v-for、v-show中的DOM节点,返回来的只能是undefined,因为在mounted阶段他们根本不存在!!

    经过检验,上面端文字是错误的,$refs定位不到的主要原因是因为v-if、v-for、v-show这些语句如果依赖父组件传来的参数的话,该该参数是在mounted()阶段子还没获取得到~~~~!!!!

    如果想要真正地在DOM加载完成后拿到数据,就需要调用VUE的全局api : this.$nextTick(() => {})

    如果说mounted阶段是加载阶段,那么updated阶段则是完成了数据更新到DOM的阶段(对加载回来的数据进行处理),此时,ref、数据等等全部都挂载到DOM结构上去,在update阶段使用this.$refs.xxx,就100%能找到该DOM节点。

    updated与mounted不同的是,在每一次的DOM结构更新,vue都会调用一次updated(){}钩子函数!而mounted仅仅只执行一次而已

    简单来说,只要在调试的时候,能看到元素的存在,在updated阶段都可以使用this.$refs.xxx找到对应的DOM节点!

4. 组件之间传递参数的五种方法

(1)路由query 使用场景:根据url地址来传参

{ 
    path: '/workTaskEdit/:id',
    name: 'workTaskEdit',
    components: '',
 },
 <router-link :to="{path:'/workTaskEdit',query{id:id}}">
 vm.$router.push({name:'workTaskEdit', query:{id:id}})

(2)props 使用场景:父子组件传参

     //子组件
     props: [ 'a' ] 或者 props: {a:{type: 'Array'}}
      //父组件
     <component  :a= 'data'> </component>

(3)vue Bus 使用场景:简单的非父子组件传参

aa组件和bb组件为非父子组件,假设 bb 组件里面有个按钮,点击按钮,把 123 传递给 aa 组件
在根组件定义Bus,用一个空的Vue实例作为中央事件总线,注册到根组件,避免局部作用域的影响。

// 根组件(this.$root)
new Vue({
  el: '#app',
  router,
  render: h => h(App),
  data: {
   // 空的实例放到根组件下,所有的子组件都能调用
    Bus: new Vue()
  }
})

bb 组件内调用事件触发

<button @click="submit">提交<button>

methods: {
   submit() {
     // 事件名字自定义,用不同的名字区别事件
      this.$root.Bus.$emit('eventName', 123)
    }
 }

aa 组件内调用事件接收↓
  // 当前实例创建完成就监听这个事件
  created(){
    this.$root.Bus.$on('eventName', value => {
      this.print(value)
    })
  },

  methods: {
    print(value) {
      console.log(value)
    }
  },

  // 在组件销毁时别忘了解除事件绑定
  beforeDestroy() {
     this.$root.Bus.$off('eventName')
  },
         补充: 利用Bus可以在Vue外部调用methods里面的方法:

                   在父组件中的methods的方法中定义一个方法,在方法里用$emit接收公共组件里的方法,注意父组件要用一个变量保存,var vm = new Vue({})

                   

var vm = new Vue({  
    el: '#example',  
    data: {  
        msg: 'Hello Directive',  
        data: {}  
    },  
    methods: {  
        getCardNum: function (data, on) {  
            eventHub.$emit('translate', data);  
        }  
    }  
});  


在事件当前的组件中,在created中,用$on向公共的组件eventHub传递,translate是自定义的,getCardNum(data)是要在外部调用的方法;    

eventHub.$on('translate', function (data) {  
                that.getCardNum(data);  
}); 

最后就可以在vue组件外部,或者文件外部调用getCardNum(data)这个函数,比如在html中就可以 onclick = vm.getCardNum() 这样来调用;vm是父组件

(4)vuex 使用场景:复杂的非父子组件传参,各个组件共享数据

(5) 复杂的项目中一般用vuex 参考 https://vuex.vuejs.org/zh-cn/api.html

5. 组件和route使用$router.params.xx耦合度太高,可尝试使用props解耦

//通过 props 解耦
const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

//这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。

补充: 切记router.push的时候不要path和params一起用,params会失效,建议用query,如果非要用params,可以使用组件的name。而且一定要注意取数据的时候是route,不是router,千万不要被坑 了,打印出来可以发现route是本路由的信息,而router是全局的router信息

6. 导航守卫

完整的导航解析流程
导航被触发。
在失活的组件里调用离开守卫。
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫 (2.5+)。
导航被确认。
调用全局的 afterEach 钩子。
触发 DOM 更新。
用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

(1)组件内的守卫

beforeRouteEnter 不能访问this,但是在next里面有一个vm参数 可以访问组件实例 场景:在渲染该组件的对应路由被 confirm 前调用
beforeRouteUpdate 有this,不支持回调 在当前路由改变,但是该组件被复用时调用
beforeRouteLeave 有this,不支持回调 场景:这个离开守卫通常用来禁止用户在还未保存修改前突然离开

(2)路由独享的守卫
routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ]

7. mixins使用注意事项和高级用法,继承extends,二者类似

    对比: mixins接受对象数组(多继承),extends接受对象或函数(单继承)

      合并策略:
     (1)值为对象的选项,如 methods, components 和 directives 将合并到同一个对象内。如果键冲突则组件的选项优先。
     (2)同名钩子函数被并入一个数组,因而都会被调用。另外,混合的钩子将在组件自己的钩子之前调用

继承钩子函数

const extend = {
 created () {
  console.log('extends created')
 }
}
const mixin1 = {
 created () {
  console.log('mixin1 created')
 }
}
const mixin2 = {
 created () {
  console.log('mixin2 created')
 }
}
export default {
 extends: extend,
 mixins: [mixin1, mixin2],
 name: 'app',
 created () {
  console.log('created')
 }
}
控制台输出
extends created
mixin1 created
mixin2 created
created

结论: 优先调用mixins和extends继承的父类,extends触发的优先级更高,相对于是队列

push(extend, mixin1, minxin2, 本身的钩子函数)

mixins

  调用方式: mixins: [mixin1, mixin2]

  是对父组件的扩充,包括methods、components、directive等。。。

  触发钩子函数时,先调用mixins的函数,再调用父组件的函数。

  虽然也能在创建mixin时添加data、template属性,但当父组件也拥有此属性时以父为准,从这一点也能看出制作者的用心(扩充)。

  data、methods内函数、components和directives等键值对格式的对象均以父组件/实例为准

extends

  调用方式: extends: CompA

  同样是对父组件的扩充,与mixins类似,但优先级均次于父组件

extend

  扩展组件的构造器

  当我们调用vue.component('a', {...})时自动调用

  值得注意的是extend内的data为一个函数

8. model指令高级

场景: 默认情况下v-model把value用作prop且把input作为event事件,而一些输入类型比如单选框和复选框可能想使用value另做它用,这时需要修改prop来达到不同的目的。
ue.component('my-checkbox',{
    model: {
        prop: 'checked',
        event: 'change',
    },
    props: {
        value: String,
        check: {
            type: 'Number',
            default: 0,
        }
    }
})

9. is的使用

场景 :运用在动态组件,且基于dom内模版的限制下工作
value: 已注册的组件名称或者一个组件的选项对象

结合keep-alive组件使用效果更好 创建一次组件缓存,避免重复渲染

keep-alive>
    <component :is='currentTab'></component>
</keep-alive>
//变通
<table>
<tr :is='my-component'></tr>
</table>

10.利用Object.freeze()提升性能

场景: 对于纯展示的大数据

 eg:  data: { list: Object.freeze([.........]) }

11.插槽的使用

内容分发API <slot>

具名插槽:
父组件
<templete slot="header">
    <h1>.....</h1>
</templete>

或者

<h1 slot='header'>...</h1>

子组件

<header>
    <slot name='header'></slot>
</header>

默认插槽

<slot>默认值</slot>
作用域插槽传值

父组件

<templete slot-scope='{todo}'>
    {{ todo.text }}
</templete>

子组件

<slot :todo="todo">
     {{ todo.text }}
</slot>

12.Vue对变动的数组和对象检测时,需要注意的地方

数组:以下两个变动的数组不能检测到

1)利用索引直接设置一个项时,vm.items[indexOf] = newItem;

 (2) 修改数组的长度, vm.items.length = newLength;

       解决办法:

      (1) Vue.set(items,index,newValue)

           Vue.items.splice(items,1,newValue)

           this.$set(items,index,newValue)

       (2)  items.splice(newLength)

 对象:对象添加属性和删除属性时,vue不能检测到

       解决办法:

       Vue.set(object,newKey,newValue)

       this.$set(object,newKey,newValue)

       Object.assign({},object,{属性列表})

参考于大~~辉

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容

  • 这是一篇集合了从如何查看 vue-router源码(v3.1.3),到 vue-router源码解析,以及扩展了相...
    尤小小阅读 5,526评论 2 14
  • SPA单页应用 传统的项目大多使用多页面结构,需要切换内容的时候我们往往会进行单个html文件的跳转,这个时候受网...
    视觉派Pie阅读 11,828评论 1 55
  • vue-cli搭建项目 确保安装了node与npm 再目标文件夹下打开终端 执行cnpm i vue-cli -g...
    Akiko_秋子阅读 3,223评论 1 22
  • 1.css只在当前组件起作用答:在style标签中写入scoped即可 例如: 2.v-if 和 v-show 区...
    小棋子js阅读 515评论 0 0
  • 回忆: 我们知道,h5的history或者hash帮助我们解决了,变化url跳转页面不发送请求,并且我们能监听到u...
    LoveBugs_King阅读 3,638评论 0 5