为了使用一个模块,我们需要在两个文件中分别做一次引入操作。这其实是一件非常奇怪不合理的事!我们为什么要模块化?就是为了封装一个模块,可以做到导入它就能使用,而它是如何实现的,它有什么依赖关系完全是这个模块自己处理的,也就是上图中对 modal.css 的依赖应该是 modal.js 自己处理的。
但是我们写了N年的前端却一直这样写模块,不是因为他对,而是因为我们习惯了这种错误的方式。现在用Vue我们可以完全封装一个模块的全部依赖,无论是模板、CSS还是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 falsethis.list.unshift({title: input,done: false}) } }}</script><style lang="less" scoped>ul { margin-left: 2rem; padding: 0; .done { text-decoration: line-through; }}</style>
<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 按照 模块化 重写了一遍。模块化是构建大型应用的基础之一,但是这一点还不够,我们还需要做到:
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 = "" } }}