25. 单文件组件——.vue文件

137 阅读3分钟

1. 后缀名.vue文件

在很多 Vue 项目中,我们使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素。

这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图。但当在更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:

  • 全局定义 (Global definitions)  强制要求每个 component 中的命名不得重复
  • 字符串模板 (String templates)  缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 ``
  • 不支持 CSS (No CSS support)  意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤 (No build step)  限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

文件扩展名为 .vue 的 single-file components (单文件组件)  为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。

这是一个文件名为 Hello.vue 的简单实例

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
// export default {}
module.exports = {
  name: "Hello",
  data() {
    return {
      greeting: "Hello"
    };
  },
  created() {},
  methods: {}
};
</script>
<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

现在我们获得:

我们可以使用预处理器来构建简洁和功能更丰富的组件,比如 Pug,Babel (with ES2015 modules),和 Stylus。

可以只是简单地使用 Babel,TypeScript,SCSS,PostCSS - 或者其他任何能够帮助你提高生产力的预处理器。如果搭配 vue-loader 使用 webpack,它也能为 CSS Modules 提供头等支持。

1.1 单文件组件.vue关注点分离

关注点分离不等于文件类型分离。 在现代 UI 开发中,我们已经发现相比于把代码库分离成三个大的层次并将其相互交织起来,把它们划分为松散耦合的组件再将其组合起来更合理一些。在一个组件里,其模板、逻辑和样式是内部耦合的,并且把他们搭配在一起实际上使得组件更加内聚且更可维护。

在单文件组件.vue中,你仍然可以把 JavaScript、CSS 分离成独立的文件然后做到热重载和预编译。

<!-- my-component.vue -->
<template>
  <div>This will be pre-compiled</div>
</template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>

image.png

image.png

2. 单文件组件.vue起步

2.1 todo 例子

1.gif

<!-- TodoList.vue -->
<template>
  <div>
    <h1>My Todo App!</h1>
    <BaseInputText
      v-model="newTodoText"
      placeholder="New todo"
      @keydown.enter="addTodo"
    />
    <ul v-if="todos.length">
      <TodoListItem
        v-for="todo in todos"
        :key="todo.id"
        :todo="todo"
        @remove="removeTodo"
      />
    </ul>
    <p v-else>
      Nothing left in the list. Add a new todo in the input above.
    </p>
  </div>
</template>

<script>
import BaseInputText from "./BaseInputText.vue";
import TodoListItem from "./TodoListItem.vue";

let nextTodoId = 1;

export default {
  components: {
    BaseInputText,
    TodoListItem
  },
  data() {
    return {
      newTodoText: "",
      todos: [
        {
          id: nextTodoId++,
          text: "Learn Vue"
        },
        {
          id: nextTodoId++,
          text: "Learn about single-file components"
        },
        {
          id: nextTodoId++,
          text: "Fall in love"
        }
      ]
    };
  },
  methods: {
    addTodo() {
      const trimmedText = this.newTodoText.trim();
      if (trimmedText) {
        this.todos.push({
          id: nextTodoId++,
          text: trimmedText
        });
        this.newTodoText = "";
      }
    },
    removeTodo(idToRemove) {
      this.todos = this.todos.filter(todo => {
        return todo.id !== idToRemove;
      });
    }
  }
};
</script>
<!-- BaseInputText.vue -->
<template>
  <input type="text" class="input" :value="value" v-on="listeners" />
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ""
    }
  },
  computed: {
    listeners() {
      return {
        // Pass all component listeners directly to input
        ...this.$listeners,
        // Override input listener to work with v-model
        input: event => this.$emit("input", event.target.value)
      };
    }
  }
};
</script>

<style scoped>
.input {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid blue;
}
</style>
<!-- TodoListItem.vue -->
<template>
  <li>
    {{ todo.text }}
    <button @click="$emit('remove', todo.id)">
      X
    </button>
  </li>
</template>

<script>
export default {
  props: {
    todo: {
      type: Object,
      required: true
    }
  }
};
</script>

2.2 针对刚接触 JavaScript 模块开发系统的用户

有了 .vue 组件,还需要学会使用一些附加的工具:

了解这些资源之后,参考阅读 Vue CLI 3。能很快地运行一个带有 .vue 组件、ES2015、webpack 和热重载 (hot-reloading) 的 Vue 项目!

2.3 针对高级用户

CLI 会为你搞定大多数工具的配置问题,同时也支持细粒度自定义配置项

如果想从零搭建你自己的构建工具,这时你需要通过 Vue Loader 手动配置 webpack。更多 webpack 的内容,请查阅其官方文档和 Webpack Academy