1. 程式人生 > 程式設計 >vue單檔案元件的實現

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-loaderhttp://www.cppcns.com"
      },{
        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.jshttp://www.cppcns.com 和TodoItem.vue

其中:App.vue

<template>
  <div class="wrapper">
    <h1>My Todo List</h1>
    <form @submit.prevent="addTodo">
      <iSpRQHrpiIwnput 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 B程式設計客棧linkMacSystemFont,-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

  swww.cppcns.compan
    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單檔案元件內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!