Vue项目经验

1、动态添加class或style

有时候希望根据某个状态值来决定是否添加某个类,可以这样:

// 数组语法
<div :class="['a',value==true?'b':'']">             //类a总是存在,当value为真时类b存在
// 对象语法
<div :class="{'a':true,'b':value}">            //类a,b都由value控制
<div :style="{background:'url('+value.url+')'}">    //对于style,最好用对象语法,属性值取自当前vue实例

2、绑定事件函数

有两种方式:

// 方式1
<div @click='handle'></div>         //handle函数的this指向当前vue实例,并且会将event对象传入handle作第一个参数,通常可用event.target获取被点击的DOM元素
// 方式2
<div @click='handle("param1",$event)'></div>         //handle函数的this指向当前实例,也可将$event传入handle,作用同上,一般用来获取被点击的DOM元素

3、关于箭头函数

vue的一些配置项,如mounted,beforeRouteEnter,beforeRouteLeave等,本身是一个函数,若使用箭头函数语法,则其内的this对象不是当前vue实例。因此这三个配置项不要用箭头函数,其它的配置项,如methods,computed,watch等接受对象作为配置的,其内的函数最好用箭头函数,另外setTimeout(fn,time)中的fn最好也用箭头函数。

4、数据传递

1、父子组件(prop down,event up)

  • 一般而言,父组件通过prop传递属性给子组件,子组件事先需定义props属性,如果没有定义props,那这个传递过来的值会自动添加到子组件的根元素上;
  • 子组件中一般通过触发某种自定义事件向父元素通信,因此一般在父元素中使用子组件的地方监听该事件名,该事件名是自定义的,因此要与子组件的一致。
<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
  • 如果要在父元素中使用子组件的地方监听原生事件,可用@click.native
  • 普通html标签上用@click是原生事件,自定义组件中用@click是自定义事件

2、非父子组件
有时候,非父子关系的两个组件之间也需要通信。在简单的场景下,可以使用一个空的 Vue 实例作为事件总线:

var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
/ 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
  // ...
}.bind(this))//绑定作用域

5、不能触发视图更新的操作

  • 数组
// 无法触发
vm.items[indexOfItem] = newValue
vm.items.length = newLength
// 可以触发
Vue.set(example1.items, indexOfItem, newValue)
example1.items.splice(newLength)
  • 对象
var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})
Vue.set(vm.userProfile, 'age', 27) //响应式

3、watch对象变化:

              'messages': {   // messages是一个数组,监听数组或对象的变化
            handler: function(newValue, old) {
                if (newValue.length == 0) {
                    setTimeout(() => {
                        history.back();
                    }, 1000)
                }
            },
            deep: true
        }

6、关于beforeRouteEnter和beforeRouteLeave

  • beforeRouteEnter(to,from,next) 无法访问当前vue实例,一般用于从localStorage中读取数据恢复页面,比如从页面a到b,在从b回到a,若a有数据变化(ajax拉取)就更新页面,否则不更新;或者还原当前滚动条位置
  • beforeRouteLeave(to,from,next) 可访问当前vue实例,一般用于页面离开时将某些状态保存在localStorage中,例如当前滚动条位置
  • 二者都必须显示调用next函数

7、keep-alive和router-view混合使用

// 这是目前用的比较多的方式
<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

... 
  routes: [
    { path: '/', redirect: '/index',  component: Index, meta: { keepAlive: true }},
    {
      path: '/common',
      component: TestParent,
      children: [
        { path: '/test2', component: Test2, meta: { keepAlive: true } } 
      ]
    }
    ....
    // 表示index和test2都使用keep-alive

若有三个页面A->B->C,希望A->B时B拉新数据;C返回B时B不拉新数据,且保存B到C之前的位置,该如何做?
1、给B设置keepAlive:true;
2、在B的beforeRouteEnter中做获取数据的操作,该钩子的三个参数可以判断B的上一个页面,从而决定是否拉数据;
在B的beforeRouteLeave里用localStorage记录位置,并清除和页面展现相关的数据状态;
3、在接口回调里最好用EventCenter这种观察者模式来触发数据获取成功的事件
4、在B的mounted钩子里接受数据获取成功的事件,就可以拿到数据
5、在B的activated钩子里取localStorage里的位置

8、router-link常用方式

<!-- 命名的路由 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
<!-- 带查询参数,下面的结果为 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>

//对应js
this.$route.params.userId
this.$route.query.plan

9、过度动画

  • 单元素过渡
    transition包一个含有v-if或v-show指令的元素就行


    动画过程
  • 多元素过渡
    transition下只能有一个根元素,所以多元素时必须用v-if,v-else来保证只有一个,此外,多个相同标签时,最好给每个标签设置key
  • 多组件过渡
    用动态组件即可
  • 列表过渡
    需用transition-group组件包裹,被包裹元素必须设置key,其他和单元素一样

10、数据拉取

  • 数据获取操作最早可以放在每个组件的beforeRouteEnter里,然后利用观察者模式把数据传到beforeMount钩子函数里做逻辑操作
     beforeRouteEnter(to, from, next) {
        let feed_id = to.params.id;
        if (from.name != 'CommentDetail' && feed_id) {
            getArticleDetail({
                feed_id: feed_id
            }).then(res => EventCenter.$emit('renderArticle', res));

            getComment({
                feed_id: feed_id
            }).then(res => EventCenter.$emit('renderComment', res));
        }
        next();
      }

     beforeMount(){
           // 1、渲染文章详情
       EventCenter.$on('renderArticle', res => {
            
       });
       // 2、渲染文章评论
       EventCenter.$on('renderComment', res => {
            
       })
      }  

11、插件 or 组件

对于全局使用频率较高的最好写成插件,而不是组件。例如loading,toast之类,若写成组件,则需要在每个父组件中引入该组件,比较麻烦,写成插件就可以直接调用

import $ from 'n-zepto';
import Vue from 'vue';
export default {
    install() {
        var timer = null;
        Vue.prototype.$alerTip = function(text, delay, options) {
            var defaultCssObject = {
                position: 'fixed',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                zIndex: 10000,
                minWidth: '1.5rem',
                margin: '0 auto',
                left: 'calc((100% - 1.5rem) / 2)',
                bottom: '1.4rem',
                borderRadius: '.1rem',
                height: '.5rem',
                lineHeight: '.5rem',
                background: '#000',
                opacity: 0.85,
                color: 'white',
            };
            var cssObject = options || defaultCssObject;
            var temp = `<div class="v-toast">${text}</div>`;
            var $div = $(temp);
            $div.css(cssObject);
            $('body').append($div);
            clearTimeout(timer);
            timer = setTimeout(() => {
                $('.v-toast').remove();
                timer = null;
            }, delay);
        }
    }
}

12、vue父子组件生命周期执行顺序问题

父子组件钩子执行顺序:
father created
father before mounted
son created
son before mount
son mounted
father mounted
所以,你在父组件 beforeMounted 中已初始化的值一定可以通过props下发到子组件

13、vue-router切换页面滚动条位置问题

注意:scrollBehavior只有在history.pushState可用时才有效,而该方法仅兼容到IE10(移动端可放心使用)

const router = new VueRouter({
    routes,
    scrollBehavior (to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition
        } else {
            if (from.meta.keepAlive) {       // 通过meta属性精细控制
                from.meta.savedPosition = document.body.scrollTop;
            }
            return { x: 0, y: to.meta.savedPosition || 0 }
        }
    }
})

14、vue库的引入问题

vue提供众多版本库文件,在使用*.vue单文件组件的开发模式时,只需引入vue.runtime.min.js即可,该库体积最小,不过要注意初始化vue实例时,不能用template选项,而应该换为render,其他组件中的template会被vue-loader转化为类似h=>h(Component)这种形式,本质上是将模板编译的时机从运行时转移到了编译时(只有初始化时不能用template选项,因为vue-loader 和 vue 都无法识别)

                new Vue({
            el: '#app',
            router,
            render: h => h(App)
        })
              // webpack对应修改:
                alias: {
            'vue$': 'vue/dist/vue.runtime.min.js'
        }

15、三种路由钩子执行顺序问题,
全局:beforeEach,beforeResolve,afterEach
组件:beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave
路由:beforeEnter
调用要离开路由的组件守卫beforeRouteLeave
调用全局前置守卫:beforeEach
在重用的组件里调用 beforeRouteUpdate
调用路由独享守卫 beforeEnter。
解析异步路由组件。
在将要进入的路由组件中调用beforeRouteEnter
调用全局解析守卫 beforeResolve
导航被确认。
调用全局后置钩子的 afterEach 钩子。

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

推荐阅读更多精彩内容