前端框架Vue.js构建大型应用浅析


Posted in Javascript onSeptember 12, 2016

真正的模块化

前端模块化很早就开始了,无论是 require.js,browserify 进行模块化打包, 还是 Angular 进行依赖注入,我们都可以把JS代码分成一个个小的模块并组装起来。然后我们还会通过 less 或者 sass 来把CSS文件也拆成一个个小的模块来写,甚至我们在CSS代码中感受到了 封装,继承,多态 等面向对象的特性。

然而,在 webpack 出来之前,我们所谓的模块化根本不能算作模块化。为什么这么讲,因为我们存在一个重要的问题没有解决,就是JS模块对CSS模块的依赖。

比如我们有一个JS模块 modal 那么我们直接导入并调用它就能弹出一个对话框吗?如下图所示可以吗?

理论上应该是这样的,但实际上这个 modal其实还依赖一个对应的CSS模块 modal.less ,如果不导入这个模块我们是无法弹出一个正常的对话框的,而且,导入这个CSS模块竟然不是和导入JS模块写在同一个地方,而是写在另一个CSS文件中。也就是说,其实依赖关系是这样的:

前端框架Vue.js构建大型应用浅析

为了使用一个模块,我们需要在两个文件中分别做一次引入操作。这其实是一件非常奇怪不合理的事!我们为什么要模块化?就是为了封装一个模块,可以做到导入它就能使用,而它是如何实现的,它有什么依赖关系完全是这个模块自己处理的,也就是上图中对 modal.css 的依赖应该是 modal.js 自己处理的。
但是我们写了N年的前端却一直这样写模块,不是因为他对,而是因为我们习惯了这种错误的方式。现在用Vue我们可以完全封装一个模块的全部依赖,无论是模板、CSS还是JS,我们都不需要再去关心,只要引入这个模块就可以使用,而模块的依赖是它自己进行处理的。

那么我们的依赖关系就变成了:

前端框架Vue.js构建大型应用浅析

其中 modal.vue 包含了全部所需要的依赖,那么我们就不在需要自己去处理对应的 CSS 甚至 模板了。这才是模块化应该达到的效果。

创建 Vue 项目

Vue 提供了一个工具 vue-cli 可以创建一个项目模板: https://github.com/vuejs/vue-cli

这里我先尝试了一下另一个模板项目:https://github.com/vuejs-templates/webpack

然后我们就可以不用 纯JS来写模块了,而是借助 webpack 来把一个模块相关的所有内容全部写到一个文件中。以之前的 todo list 为例,其实上一章讲的只是component的用法所以那样写了。我们改成一个更好的写法如下:

List.vue:

<template>
 <ul>
 <li v-for='todo in list'>
  <label v-bind:class="{ done : todo.done }" >
  <input type="checkbox" v-model="todo.done"/>
  {{todo.title}}
  </label>
 </li>
 </ul>

</template>

<script>
export default {
 props: {
 initList: {
  type: Array
 }
 },
 data () {
 return {
  list: []
 }
 },
 events: {
 add (input) {
  if (!input) return false
  this.list.unshift({
  title: input,
  done: false
  })
 }
 }
}
</script>

<style lang="less" scoped>
ul {
 margin-left: 2rem;
 padding: 0;

 .done {
 text-decoration: line-through;
 }
}
</style>

Form.vue:

<template>
 <h1>{{username}}'s Todo List</h1>
 <form v-on:submit="add" v-on:submit.prevent>
 <input type="text" v-model="input"/>
 <input type="submit" value='add' />
 </form>
</template>

<script>
export default {
 props: {
 username: {
  type: String,
  default: 'Unnamed'
 }
 },

 data () {
 return {
  input: ''
 }
 },

 methods: {
 add () {
  this.$dispatch('add', this.input)
  this.input = ''
 }
 }
}
</script>

Todo.vue:

<template>

 <div id="todo">
 <todo-form username='Lily'></todo-form>
 <todo-list></todo-list>
 </div>

</template>

<script>
import Form from './Form.vue'
import List from './List.vue'

export default {
 components: {
 'todo-form': Form,
 'todo-list': List
 },

 events: {
 add (input) {
  this.$broadcast('add', input)
 }
 }
}

</script>

<style>
</style>

App.vue:

<template>
 <todo></todo>
</template>

<script>
import Todo from './components/Todo.vue'

export default {
 components: {
 'todo': Todo
 }
}
</script>

<style>
</style>

这样我们就把之前的 Todo List 按照 模块化 重写了一遍。模块化是构建大型应用的基础之一,但是这一点还不够,我们还需要做到:
 •更好的状态管理,把不同组件共享的 State 独立出来管理
 •自动化测试
 •路由等 

这里我们只做其中一个,就是把 State 独立成一个单独模块。很显然,对一个 Todo List 应用来说,保存 todo list 的数据结构就是不同组件共享的 State。
之前我们为什么需要进行事件广播,就是因为不同组件之间要操作的数据就保存在 List.vue 中,所以在 Form.vue 中想增加一条数据的时候需要通过事件的方式去通知 List.vue 来添加。
也就是其实这个数据不是 List.vue 私有的,应该至少是这两个组件公有的,现在被 List.vue 据为己有之后,Form.vue 没法修改它只好通过事件进行通知。
虽然事件的方式很优雅,但其实我们可以做的更好,就是把数据独立出来,这样 Form.vue 和 List.vue 都可以直接修改数据,而不用那么麻烦发通知。

这里我们增加一个 Store.js 文件:

export default {
 list: [
 ],

 add (title) {
 if (!title) return
 this.list.unshift({
  title: title,
  done: false
 })
 }
}

 然后 我们可以把 List.vue 改成这样,这里只贴出JS部分的代码:

import Store from '../Store.js'

export default {
 props: {
 initList: {
  type: Array
 }
 },
 data () {
 return Store
 }
}

 Form.vue 也不需要广播了,直接调用 Store.add 方法既可以添加:

import Store from '../Store.js'
export default {
 props: {
 username: {
  type: String,
  default: 'Unnamed'
 }
 },

 data () {
 return {
  input: ''
 }
 },

 methods: {
 add () {
  Store.add(this.input)
  this.input = ''
 }
 }
}

 这样一改之后,整个逻辑会清晰很多,并且应用越是复杂,越是应该抽出公有的 Store ,不然会出现广播事件满天飞的情况。
另外用这个项目模板之后,hot-reload 爽的不要不要的,刷新操作都省了。

上述的源码在这里:https://github.com/lihongxun945/vue-webpack-todo-list

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQuery 常用操作代码
Mar 14 Javascript
js下拉框二级关联菜单效果代码具体实现
Aug 03 Javascript
调用DOM对象的focus使文本框获得焦点
Feb 19 Javascript
js操作数组函数实例小结
Dec 10 Javascript
javascript中加var和不加var的区别 你真的懂吗
Jan 06 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(二)
Sep 14 Javascript
JavaScript中定义对象原型的两种使用方法
Dec 15 Javascript
详解网站中图片日常使用以及优化手法
Jan 09 Javascript
element vue Array数组和Map对象的添加与删除操作
Nov 14 Javascript
Angular(5.2-&gt;6.1)升级小结
Dec 27 Javascript
vue实现搜索功能
May 28 Javascript
Js图片点击切换轮播实现代码
Jul 27 Javascript
jQuery过滤选择器用法示例
Sep 12 #Javascript
强大Vue.js组件浅析
Sep 12 #Javascript
超详细的JS弹出窗口代码大全
Apr 18 #Javascript
使用JS实现图片展示瀑布流效果的实例代码
Sep 12 #Javascript
关于javascript的一些知识以及循环详解
Sep 12 #Javascript
基于AngularJS实现iOS8自带的计算器
Sep 12 #Javascript
Javascript6中字符串的四个新用法分享
Sep 11 #Javascript
You might like
PHP实现多维数组转字符串和多维数组转一维数组的方法
2015/08/08 PHP
PHP依赖注入(DI)和控制反转(IoC)详解
2017/06/12 PHP
动态控制Table的js代码
2007/03/07 Javascript
给页面渲染时间加速 干掉Dom Level 0 Event
2012/12/19 Javascript
js数组Array sort方法使用深入分析
2013/02/21 Javascript
js Map List 遍历使用示例
2013/07/10 Javascript
jquery实现树形二级菜单实例代码
2013/11/20 Javascript
js加载读取内容及显示与隐藏div示例
2014/02/13 Javascript
屏蔽相应键盘按钮操作
2014/03/10 Javascript
让checkbox不选中即将选中的checkbox不选中
2014/07/11 Javascript
JavaScript实现Java中StringBuffer的方法
2015/02/09 Javascript
js限制文本框只能输入整数或者带小数点的数字
2015/04/27 Javascript
JS使用JSON作为参数实例分析
2016/06/23 Javascript
BootStrap table删除指定行的注意事项(笔记整理)
2017/02/05 Javascript
JavaScript常见事件对象与操作实例总结
2019/01/05 Javascript
vue 设置 input 为不可以编辑的实现方法
2019/09/19 Javascript
图文讲解选择排序算法的原理及在Python中的实现
2016/05/04 Python
Python实现PS图像调整颜色梯度效果示例
2018/01/25 Python
解决nohup重定向python输出到文件不成功的问题
2018/05/11 Python
判断python字典中key是否存在的两种方法
2018/08/10 Python
python traceback捕获并打印异常的方法
2018/08/31 Python
解决pycharm工程启动卡住没反应的问题
2019/01/19 Python
Scrapy框架爬取西刺代理网免费高匿代理的实现代码
2019/02/22 Python
Python基于机器学习方法实现的电影推荐系统实例详解
2019/06/25 Python
Pytest mark使用实例及原理解析
2020/02/22 Python
python使用paramiko实现ssh的功能详解
2020/03/06 Python
借助Paramiko通过Python实现linux远程登陆及sftp的操作
2020/03/16 Python
Python虚拟环境的创建和包下载过程分析
2020/06/19 Python
HTML5 Canvas入门学习教程
2016/03/17 HTML / CSS
Booking.com英国官网:全球酒店在线预订网站
2018/04/21 全球购物
Coltorti Boutique官网:来自意大利的设计师品牌买手店
2018/11/09 全球购物
化学相关工作求职信
2013/10/02 职场文书
事业单位竞聘上岗实施方案
2014/03/28 职场文书
党员反腐倡廉学习心得体会
2015/08/15 职场文书
详解mysql三值逻辑与NULL
2021/05/19 MySQL
永中文档在线转换预览基于nginx配置部署方案
2022/06/10 Servers