vue单文件组件的实现

最近翻阅了一下vue。发觉有一个单文件组件之前基本忽视掉了。vue.js中的单文件组件允许在一个文件中定义一个组件的所有内容。也就是说,一个页面或者是一个组件,我们想将他们捆绑在一起,那么vue的这个单文件组件可以做到。正如vue的官网说的,“在很多 Vue 项目中,我们使用 app.component 来定义全局组件,紧接着用 app.mount('#app') 在每个页面内指定一个容器元素。”这里的组件,都是相对简单的,而面对一个比较复杂的项目,这种方式就行不通。原因如下:

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

所有这些都可以通过扩展名为 .vue 的 single-file components (单文件组件) 来解决,并且还可以使用 webpack 或 Browserify 等构建工具。
那么vue项目中的单文件组件需要如何创建呢?

构建

npm install -D @vue/compiler-sfc

在控制台上输入上述的代码,然后就会出现一个文件夹和另一个json文件。如下:

我们要构建单文件组件,就要自个制定文件。同时对webpack也要有一定的了解才行。
比如说,我们自己安装一些需要的依赖。比如说,css-loader、css的预编译处理器等等。因为需要项目对vue文件进行解析,那么vue-loader是必须的。

这些文件其实都是vue的简单版本。比如简单版的hello.vue文件,可以如下

由此可以看见: 三个部分组成。而template这个部分是不可缺少的,其它的两个部分,style和script还可以忽略掉。
script让你的页面js可以跟vue完美结合,而style可以使用预处理器来构建简洁和功能更丰富的组件。(这个单文件组件很像最初前端开发中的html文档,它有自己的style标签和script标签,只是表现层使用一个template标签。由于使用了简单的方式,得到一个强大的分层组件(内容/模板:,表现:

可能有的小伙伴喜欢将不同模块拆分开来,也就是vue文档说的关注点分离。那么没有关系,你可以拆开那些文档,将css和js拆开到另一个文件,之后引入进组件中。如下:

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

项目大致目录如下:

其中,index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Vue Simple Todo App with SFC</title>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" rel="external nofollow"  rel="external nofollow"
    />
    <link rel="stylesheet" href="/dist/main.css" rel="external nofollow"  rel="external nofollow"  />
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/main.js"></script>
  </body>
</html>

package.json

{
    "private": true,
    "scripts": {
      "dev": "webpack-dev-server",
      "build": "webpack --env.prod"
    },
    "dependencies": {
      "vue": "^3.1.1"
    },
    "devDependencies": {
      "@vue/compiler-sfc": "^3.1.1",
      "css-loader": "^3.5.2",
      "file-loader": "^6.0.0",
      "mini-css-extract-plugin": "^0.9.0",
      "stylus": "^0.54.7",
      "stylus-loader": "^3.0.2",
      "url-loader": "^4.1.0",
      "vue-loader": "^16.0.0-alpha.3",
      "vue-style-loader": "^4.1.2",
      "webpack": "^4.42.1",
      "webpack-cli": "^3.3.11",
      "webpack-dev-server": "^3.10.3"
    },
    "keywords": ["todo", "vue"],
    "name": "vue-todo-list-app-with-single-file-component",
    "description": "A simple todo list application written in Vue with Single File Component (SFC) support."
  }

webpack.config.js

const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = (env = {}) => ({
  mode: env.prod ? "production" : "development",
  devtool: env.prod ? "source-map" : "cheap-module-eval-source-map",
  entry: [
    env.prod ? false : require.resolve(`webpack-dev-server/client`),
    path.resolve(__dirname, "./src/main.js")
  ].filter(Boolean),
  output: {
    path: path.resolve(__dirname, "./dist"),
    publicPath: "/dist/"
  },
  resolve: {
    alias: {
      // this isn't technically needed, since the default `vue` entry for bundlers
      // is a simple `export * from '@vue/runtime-dom`. However having this
      // extra re-export somehow causes webpack to always invalidate the module
      // on the first HMR update and causes the page to reload.
      vue: "@vue/runtime-dom"
    }
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: "vue-loader"
      },
      {
        test: /\.png$/,
        use: {
          loader: "url-loader",
          options: { limit: 8192 }
        }
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: { hmr: !env.prod }
          },
          "css-loader"
        ]
      },
      {
        test: /\.stylus$/,
        use: ["vue-style-loader", "css-loader", "stylus-loader"]
      },
      {
        test: /\.pug$/,
        loader: "pug-plain-loader"
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: "[name].css"
    })
  ],
  devServer: {
    inline: true,
    hot: true,
    stats: "minimal",
    contentBase: __dirname,
    overlay: true,
    injectClient: false,
    disableHostCheck: true
  }
});

test.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Vue Simple Todo App with SFC</title>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" rel="external nofollow"  rel="external nofollow"
    />
    <link rel="stylesheet" href="/dist/main.css" rel="external nofollow"  rel="external nofollow"  />
  </head>
  <body>
    <div id="app222">test pages</div>
    <script src="/dist/main.js"></script>
  </body>
</html>

src文件夹里边有三个文件,App.vue main.js 和TodoItem.vue

其中:App.vue

<template>
  <div class="wrapper">
    <h1>My Todo List</h1>
    <form @submit.prevent="addTodo">
      <input type="text" name="todo-text" v-model="newTodoText" placeholder="New todo">
    </form>
    <ul v-if="todos.length">
      <TodoItem v-for="todo in todos" :key="todo.id" :todo="todo" @remove="removeTodo"/>
    </ul>
    <p class="none" v-else>Nothing left in the list. Add a new todo in the input above.</p>
  </div>
</template>

<script>
import TodoItem from "./TodoItem.vue"

let nextTodoId = 1

const createTodo = text => ({
  text,
  id: nextTodoId++
})

export default {
  components: {
    TodoItem
  },

  data() {
    return {
      todos: [
        createTodo("Learn Vue"),
        createTodo("Learn about single-file components"),
        createTodo("Fall in love ❤️")
      ],

      newTodoText: ""
    }
  },

  methods: {
    addTodo() {
      const trimmedText = this.newTodoText.trim()

      if (trimmedText) {
        this.todos.push(createTodo(trimmedText))
      }

      this.newTodoText = ""
    },

    removeTodo(item) {
      this.todos = this.todos.filter(todo => todo !== item)
    }
  }
}
</script>

<style lang="stylus">
*, *::before, *::after
  box-sizing border-box

html, body
  font 16px/1.2 BlinkMacSystemFont, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif
  padding 10px

.wrapper
  width 75%
  margin 0 auto

form
  margin-bottom 20px

input[type="text"]
  width 100%
  padding 10px
  border 1px solid #777

ul, li
  margin 0
  padding 0

p.none
  color #888
  font-size small
</style>

main.js

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

TodoItem.vue

<template>
  <li>
    <span>{{ todo.text }}</span>
    <button @click.prevent="$emit('remove', todo)">Remove</button>
  </li>
</template>

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

<style lang="stylus" scoped>
li
  display flex
  margin 5px 0

  span
    flex 1

  button
    border 1px solid orange
    background orange
    color white
    font-size 0.8rem
    padding 2px 4px
    cursor pointer

    &:hover
      border-color #ff8100
      background #ff8100
</style>

注意
如果不懂得webpack,建议还是按照官网的指示,用vue的脚手架安装基本的工具。
或者是按照我给的pakage.json放到项目上,npm install一下,安装好最基本的环境,然后可以通过npm run dev进行本地开发。

其实,我觉得这个单文件组件用处已经比较小。除非就是一个纯js的项目,用的库和组件都已经非常的古老,那么这个时候用这个单文件组件来进行新的功能开发,效果还是不错的,前提是你要对vue比较熟悉。同时,我建议还是要学习一下webpack。不要对bable都一窍不通,然后还要通过node去启动项目。

其实,用一个文件就将html/css/JavaScript分层管理,统一到了一个文件,着实能够让我们的项目看起来更加的有条理,规范性更加好。因为我们的jq时代,常常会将css混杂在html中,而且,简单的一个点击事件都要将它们分割开,这体验当然没有“分层而治”那么分明。

参考文献:
1、https://v3.cn.vuejs.org/guide/single-file-component.html#%E5%9C%A8%E7%BA%BF%E6%BC%94%E7%A4%BA
2、https://www.cnblogs.com/houxianzhou/p/14510450.html

到此这篇关于vue单文件组件的实现的文章就介绍到这了,更多相关vue单文件组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue单文件组件无法获取$refs的问题

    记录一下学习webpack+vue碰到的一个大坑,踩这个坑是我才疏学浅的表现,特此引以为戒.因为该坑实在是太坑了! 代码 header.html <body> <div id="popup-wrap"> <popup ref="popup"></popup> </div> </body> header.js import popup from '../../components/popup/po

  • vuejs 单文件组件.vue 文件的使用

    vuejs 自定义了一种.vue文件,可以把html, css, js 写到一个文件中,从而实现了对一个组件的封装, 一个.vue 文件就是一个单独的组件.由于.vue文件是自定义的,浏览器不认识,所以需要对该文件进行解析. 在webpack构建中,需要安装vue-loader 对.vue文件进行解析.在 sumlime 编辑器中,我们 书写.vue 文件,可以安装vue syntax highlight 插件,增加对文件的支持. 用vue-cli 新建一个vue项目,看一下.vue文件长什么样

  • Vuejs实现带样式的单文件组件新方法

    本文实例为大家分享了Vuejs实现单文件组件的方法,供大家参考,具体内容如下 代码如下: example.html <script src="vue.js"></script> <div id="example"> <h3>Vue component<h3> <counter></counter> <counter></counter> </div>

  • Vuejs 单文件组件实例详解

    在之前的实例中,我们都是通过 Vue.component 或者 components 属性的方式来定义组件,这种方式在很多中小规模的项目中还好,但在复杂的项目中,下面这些缺点就非常明显了: 字符串模板:缺乏高亮,书写麻烦,特别是 HTML 多行的时候,虽然可以将模板写在 html 文件中,但是侵入性太强,不利于组件解耦分离. 不支持CSS:意味着当 HTML 和 JavaScript 组件化时,CSS明显被遗漏了 没有构建步骤:限制只能使用 HTML 和 ES5 JavaScript,而不能使用

  • 详解Vue 单文件组件的三种写法

    JS构造选项写法 export defaul { data, methods, ...} JS class写法 @Component export default class Cpn extends Vue{ counter = 0 //data add(){ //methods this.counter += 1 } } TS class写法 @Component export default class Cpn extends Vue{ @Prop(Number) sum : number

  • Vue单文件组件的如何使用方式介绍

    在很多 vue项目中,我们使用 vue.component 来定义全局组件,紧接着用 new vue(el: ")在每个页面内指定一个容器元素 这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图. 但挡在更复杂的项目中,或者你的前端完全由 javascript 驱动的时候,下面这些缺点将变得非常明显: 全局定义 (global definitions)强制要求每个 component 中的命名不能重复 字符串模板 (string template

  • 解决vue 单文件组件中样式加载问题

    在写单文件组件时,一般都是把标签.脚本.样式写到一起,这样写个人感觉有点不够简洁,所以就想着把样式分离出去. 采用import加载样式 在局部作用域(scoped)采用@import加载进来的样式文件,想法是美好的.以为这样加载进来的样式文件也只对当前组件有效:可现实是残酷的,这样加载进来的样式无法限制其作用域. <style scoped> @import "样式文件"; </style> 解决方案 采用 src属性加载样式. <style src=&q

  • 基于Vue单文件组件详解

    本文将详细介绍Vue单文件组件 概述 在很多 Vue 项目中,使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素. 这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图.但当在更复杂的项目中,或者前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显: 1.全局定义 (Global definitions) 强制要求每个 compon

  • vue单文件组件的实现

    最近翻阅了一下vue.发觉有一个单文件组件之前基本忽视掉了.vue.js中的单文件组件允许在一个文件中定义一个组件的所有内容.也就是说,一个页面或者是一个组件,我们想将他们捆绑在一起,那么vue的这个单文件组件可以做到.正如vue的官网说的,"在很多 Vue 项目中,我们使用 app.component 来定义全局组件,紧接着用 app.mount('#app') 在每个页面内指定一个容器元素."这里的组件,都是相对简单的,而面对一个比较复杂的项目,这种方式就行不通.原因如下: 全局定

  • Vue单文件组件基础模板小结

    背景 相信大家在使用Vue开发项目时,基本都是以单文件组件的形式开发组件的,这种方式好处多多: 1.代码集中,便于开发.管理和维护 2.可复用性高,直接将vue文件拷贝到新项目中 我暂时就想到这两点,童鞋们可以在评论里帮我补充:因为有这么多优点,所以决定有必要将vue组件的常用配置项提炼出来,形成一个组件模板,方便日后项目开发复用 <template> <div> <h1>{{title}}</h1> <ChildComponents></

  • Vue单文件组件开发实现过程详解

    第一步:配置环境 安装cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org 安装@vue/cli cnpm install -g @vue/cli 检查版本是否正确 vue --version 使用vue.server和vue.build对*.vue文件进行快速原型开发,需要安装vue serve cnpm install -g @vue/cli-service-global 新建一个App.vue文件测试安装是否

  • vue单文件组件lint error自动fix与styleLint报错自动fix详解

    问题描述 之前用Vue(多入口打包成多页)的项目,要修改迁移并修改为一个单页应用,且使用Vue脚手架生成项目,要对js,vue,css文件的代码做lint,在修改Webpack配置后第一次跑lint居然报了几万个Error,且是在eslint加了--fix选项的情况下,且错误大多集中在顺序问题上也就是vue/order-in-components和order/properties-order的错误.以下是问题的解决方式及过程记录. stylelint中css属性顺序错误 .stylelint的配

  • vue实现一个单文件组件的完整过程记录

    目录 前言 单文件组件 基本概念 简单的loader 解析组件内容 注册组件 获取脚本内容 Data URI和Object URI 动态导入 实现 行为层 兼容性问题及其他 总结 前言 前端开发人员只要了解过vue.js框架可能都知道单文件组件.vue.js中的单文件组件允许在一个文件中定义一个组件的所有内容.这是一个非常有用的解决方案,在浏览器网页中已经开始提倡这种机制.但是不幸的是,这个概念自从2017年8月被提出以来,到现在没有任何进展,像是已经要消亡了一样.然而,深入研究这个主题并试着使

随机推荐