侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 128471 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Vue+Webpack开发可复用的单页面富应用教程(技巧篇)

2024-04-27 星期六 / 0 评论 / 0 点赞 / 2 阅读 / 12102 字

本文首发于TalkingCoder,一个有逼格的程序员社区。转载请注明出处和作者。 写在前面 本文为系列文章,总共分四节,建议按顺序阅读: 《Vue+Webpack使用规范》 《Vue+Webpa

本文首发于TalkingCoder,一个有逼格的程序员社区。转载请注明出处和作者。

写在前面

本文为系列文章,总共分四节,建议按顺序阅读:

 

《Vue+Webpack使用规范》

《Vue+Webpack开发可复用的单页面富应用教程(配置篇)》

《Vue+Webpack开发可复用的单页面富应用教程(组件篇)》

《Vue+Webpack开发可复用的单页面富应用教程(技巧篇)》

 

在前两节中,我们介绍了在项目https://github.com/icarusion/vue-vueRouter-webpack中如何配置webpack以及使用vue的组件化开发SPA。通过前两节的介绍,现在应该可以轻松的写几个路由和组件了吧,并且在这个工程的基础上开发会省去很多事情,我们的重心应该在组件的开发上。随着项目的不断扩大和对vue的学习程度不同,多少会出现一些复杂的问题或者编程的最佳实践,本篇文章就来介绍一些笔者在几个项目中积累的一些经验谈。

山寨的Vuex

说到山寨,我们先看看正版的Vuex是个什么鬼。

当你的Vue项目比较大,或者说组件使用的比较深度(层级、复用性)的时候,状态、数据越来越共享化,如果把数据都存在组件中,会变的不太好维护,那么Vuex就是在这种情况下出现的。它借鉴于Flux和Redux的设计思想,用过React的话应该不会陌生,不过要比那些简单,而且转为Vue量身打造。

为什么需要山寨的Vuex

既然已经有正版的Vuex了,而且也不复杂,为什么需要山寨的vuex呢?我们先介绍一下什么是山寨的Vuex,其实这里有点概念化了,山寨的vuex并没有什么特殊,只是一个普通的js文件,为了达到类似的目的,我们将其命名为了vuex.js。它的结构大概是这样的:

exports.POILIST = {    data : {        list: []    },    clear : function() {        this.data = {            list: []        };    }};exports.MENU = {    data: {        list: [],        select: {}    },    clear: function() {        this.data = {            list: [],            select: {}        };    }};

可以看到它对外导出了一些模块,模块的结构都有一个data,这个data里存储的就是我们需要共享的数据了,组件在通信时,不直接向上或向下传递数据,而是保存在vuex.js里,出于对象的引用关系,vue可以天然的实现共享数据的双向绑定,在使用时是这样的:

<template>    <div>        <li v-for="item in menu.list">{{ item }}</li>        <p v-for="item in poi.list">{{ item.lng }},{{ item.lat }}</p>    </div></template><script>    import { POILIST, MENU } from '../config/vuex.js';    export default {        data() {            return {                menu: MENU.data,                poi: POILIST.data            }        },        methods: {            set() {                MENU.list.push('首页');                POILIST.list.push({                    lng: 124.1,                    lat: 42.3                });            }        }    }</script>

这种存储,可以理解为相当于存在全局,并且可以加一些事件来操作数据,比如我们demo的clear()用来清空数据,只不过这里是用函数来调用,也可以将其改为观察者模式,用事件派发的形式来修改数据,其实vuex正是这么玩的。

有了vuex.js,组件之间就可以直接保存到它里面,这对数据持久化、数据共享来说是一个简单粗暴的解决方案。它适用了不大的项目(当然在大型项目中它也没什么问题),或者不太想在当下接入vuex的架构,或者想简单而又优雅地维护一些全局状态和数据。

我们知道,JavaScript的对象是引用关系,也正是这个原因我们可以设计vuex.js,但也正是这个原因,如果你的数据不是对象,只是简单数组、数字、字符串等,是不能直接共享的,JS会复制来使用,所以我们在组件的data里,return的都是像MENU.data而不是MENU.data.list,在遍历时,才循环menu.list

到底用prop还是事件

上一篇文章介绍过,组件之间数据交互,主要有prop事件两种,在使用过程中发现,这两种方案有时候都可以达到同样的效果,比如下面的这个场景,父组件向子组件传递的一个数据id,子组件来判断这个id如果发生变化,就做一些事情。用prop是这么实现的:

<!-- 父组件 --><template>    <child :id="id"></child></template><script>    export default {        data() {            return {                id: 1            }        },        ready() {            setTimeout(() => {                this.id = 2;            }, 1000);        }    }</script><!-- child组件 --><template>    </template><script>    export default {        props: {            id: {                type: Number,                default: 0            }        },        data() {            return {}        },        methods: {            handler() { }        },        watch: {            id() {                this.handler();            }        }    }</script>

child组件通过watch来监听id的改变,如果用事件可以这样实现:

<!-- 父组件 --><template>    <child :id="id"></child></template><script>    export default {        data() {            return {                id: 1            }        },        ready() {            setTimeout(() => {                this.$broadcast('child-set-data', {                    id: 2                });            }, 1000);        }    }</script><!-- child组件 --><template>    </template><script>    export default {        props: {            id: {                type: Number,                default: 0            }        },        data() {            return {}        },        methods: {            handler() { }        },        events: {            id() {                this.handler();            }        }    }</script>

这次是通过events来接收事件,效果是一样的。既然两者都可以,那如何优雅的选择呢,笔者觉得可以在数据功能上进行区分。比如上面的场景,是由子组件child在知道id改变后,在它自己的作用域里去执行handler()方法,handler可能是一个需要ajax获取数据并渲染到child组件上的函数,这时候就推荐用watch来监听id的改变了。如果handler所对应的ajax任务是在父组件完成的,父组件需要将拿到的一堆json数据传给child时,这时可以通过事件来传递。不过说到底,还是看使用者的习惯了,这两者都是可以的,只不过事件更多的是来触发外部环境的action。

管好组件的一亩三分地

用好prop的sync

上篇文章介绍过,组件间可以通过prop传递数据,而且使用sync还可以双向绑定:

<child :id.sync="id"></child>

这样id就是一个双向的了,子组件修改后,父组件也会修改,但这样做有时候会分不清到底是谁改的,也很可能会不小心就修改了父组件的值,所以在使用时要特别注意。

验证prop

有时为了省事,在写组件时就将props直接写一个数组了,但如果你写的组件是给别人用的,那还是建议对每个prop都进行严格的验证,确保他人正确使用。具体验证方法可查看文档。

子组件不该干涉父组件的状态

子组件可以通过$parent访问到父组件,也就是说它有权利去修改父组件的状态,但不建议这样做,虽然有时候是省事了,但是这让父子组件之间紧耦合,很难知道到底是谁来维护数据,而且你开发给别人用的组件,随意修改了父级的状态,这会让使用者不知所措。所以子组件应该只关心自己的数据,要修改父级状态时,通过$dispatch()方法派发事件来通知父级,由父级自己修改。

总结

一口气写完这4篇文章,还真是有点辛苦啊。半年前还只是用Vue的一些简单功能,到现在TalkingData的多条产品线已逐步使用这整套架构,这个推动的过程还是很快的,也是真正意识到Vue是一个很出色的框架,对它充满了信心。TalkingData接下来陆续上线的几个重磅产品(Data Cloud、Marketing Cloud),也是证明了这一点。很快Vue2.0也要来了,对它更是充满了期待。前端就是这么神奇,总有玩不尽的框架,但一定要选对一个适合自己的技术栈。

广告 广告

评论区