【Vue 入門】使用 Vue2 開發一個展現項目列表的應用

前言

一直沒有找到一個合適的展現我的項目的模板,因此本身動手使用 Vue 寫了一個。該模板基於 Markdown 文件進行配置,只須要按必定規則編寫 Markdown 文件,而後使用一個 在線工具 轉爲 JSON 文件便可。下面是該項目的在線地址和源碼。本文主要記錄一下項目中用到的相關知識。css

在線演示    源碼html

效果

程序最終的效果以下圖所示:vue

整個項目只包含兩個組件:項目介紹 和 側邊導航,邏輯比較簡單,十分適合入門。node

環境配置

這裏咱們使用 Gulp 和 Webpack 用做項目構建工具。初次使用 Gulp 和 Webpack 可能不太適應,由於它們的配置可能讓你看的一頭霧水。不過不用擔憂,這兩個畢竟只是一個工具,在初始時沒有必要特別的瞭解它們的工做原理,只要能運行起來就能夠。等到使用了一段時間以後,天然而然的就知道該如何配置了。這裏主要記錄一下項目中使用的配置,若是想要系統的學習如何使用這兩個工具,能夠參考下面的文章:webpack

Gulp 和 Webpack 集成

Gulp 和 Webpack 集成一個比較簡單的方式就是將 Webpack 做爲 Gulp 的一個 task,以下面的形式:git

var gulp = require("gulp");
var webpack = require("webpack");

gulp.task("webpack", function (callback) {
    //webpack配置文件
    var config = {
        watch: true,
        entry: {
            index: __dirname + '/src/js/index.js'
        },
        output: {
            path: __dirname + '/dist/js',
            filename: '[name].js'
        }
        //........
    };
    webpack(config, function (err, stats) {
        console.log(stats.toString());
    });
});

gulp.task('default', [ 'webpack']);

下面咱們分別介紹一下 gulp 和 webpack 的配置es6

Gulp 配置

Gulp 中主要配置了兩個任務:webpack 和 browserSync,這裏主要說一下 browserSync。browserSync 主要用來自動刷新瀏覽器。首先咱們配置須要監聽的文件,當這些文件發生改變後,調用 browserSync 使瀏覽器自動刷新頁面。下面是具體的配置github

var gulp = require("gulp");
var browserSync = require('browser-sync');

// 添加 browserSync 任務
gulp.task('browserSync', function () {
    browserSync({
        server: {
            baseDir: '.'
        },
        port: 80
    })
});

// 配置須要監聽的文件,當這些文件發生變化以後
// 將調用 browserSync.reload 使瀏覽器自動刷新
gulp.task("watch", function () {
    gulp.watch("./**/*.html", browserSync.reload);
    gulp.watch("dist/**/*.js", browserSync.reload);
    gulp.watch("dist/**/*.css", browserSync.reload);
});

// 添加到默認任務
gulp.task('default', ['browserSync', 'watch', 'webpack']);

Webpack 配置

咱們使用 webpack 進行資源打包的工做,就是說將各類資源(css、js、圖片等)交給 Webpack 進行管理,它會將資源整合壓縮,咱們在頁面中只需引用壓縮以後的文件便可。webpack 的基礎配置文件以下所示web

gulp.task("webpack", function (callback) {

    //webpack配置文件
    var config = {
        // true 表示 監聽文件的變化
        watch: true,
        // 加載的插件項
        plugins: [
            new ExtractTextPlugin("../css/[name].css")
        ],
        // 入口文件配置
        entry: {
            index: __dirname + '/src/js/index.js'
        },
        // 輸出文件配置
        output: {
            path: __dirname + '/dist/js',
            filename: '[name].js'
        },

        module: {
            // 加載器配置,它告訴 Webpack 每一種文件須要採用什麼加載器來處理,
            // 只有配置好了加載器才能處理相關的文件。
            // test 用來測試是什麼文件,loader 表示對應的加載器
            loaders: [
                {test: /\.vue$/, loader: 'vue-loader'}
            ]
        },
        resolve: {
            // 模塊別名定義,方便後續直接引用別名,無須多寫長長的地址
            // 例以下面的示例,使用時只須要寫 import Vue from "vue"
            alias: {
                vue: path.join(__dirname, "/node_modules/vue/dist/vue.min.js")
            },
            // 自動擴展文件後綴名,在引入文件時只需寫文件名,而不用寫後綴
            extensions: ['.js', '.json', '.less', '.vue']
        }
    };
    webpack(config, function (err, stats) {
        console.log(stats.toString());
    });
});

webpack 的相關配置說明能夠參考前面的給出的文章,下面說一下使用 webpack 2 遇到的坑:npm

extract-text-webpack-plugin

extract-text-webpack-plugin 會將 css 樣式打包成一個獨立的 css 文件,而不是直接將樣式打包到 js 文件中。下面是使用方法

{
    plugins: [new ExtractTextPlugin("../css/[name].css")],
    module: {
        loaders: [{
            test: /\.css$/,
            loader: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: "css-loader"
            })
        },
        {
            test: /\.less$/,
            loader: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: "css-loader!less-loader"
            })
        }
    },
}

這裏須要注意的地方就是,extract-text-webpack-plugin 在 webpack 1 和 webapck 2 中的安裝方式不一樣,須要根據使用的 webpack 版原本安裝:

# for webpack 1
npm install --save-dev extract-text-webpack-plugin
# for webpack 2
npm install --save-dev extract-text-webpack-plugin@beta

壓縮文件

使用 UglifyJsPlugin 插件能夠壓縮 css 和 js 文件,可是一開始時老是沒法壓縮文件,後來查閱了一下資料,大概是由於下面幾個緣由:

  1. uglifyjs-webpack-plugin 依賴於 uglify-js,而 uglify-js 默認不支持 ES6 語法,因此須要安裝支持 ES6 語法的 uglify-js
npm install mishoo/UglifyJS2#harmony --save
  1. webpack 2 中,UglifyJsPlugin 默認不壓縮 loaders,若是要啓動 loaders 壓縮,須要加入下面的配置:
plugins: [
  new webpack.LoaderOptionsPlugin({
    minimize: true
  })
]

若是按上面的修改了仍是不能壓縮文件,能夠試着將 node_modules 刪除,而後從新安裝依賴。

Vue

本部分主要記錄一下程序中用到的 Vue 語法,若是想要系統的學習一下 Vue.js,能夠參考下面的文章:

HelloWorld

咱們首先來看一個最簡單的 Vue 示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue Demo</title>
</head>
<body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
    {{ message }}
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!'
        }
    })
</script>

</body>
</html>

每一個 Vue 應用都會建立一個 Vue 的根實例,在根實例中須要傳入 html 標籤的 id,用來告訴 Vue 該標籤中的內容須要被 Vue 來解析。上面是一個簡單的數據綁定的示例,在運行實 {{ message }} 會被解析爲 "Hello Vue!"。

基礎

本節參考自 Vue 中文文檔,略有修改

在寫 Vue 應用以前,咱們要熟悉一下 Vue 的基本語法,主要包括數據綁定、事件處理、條件、循環等,下面咱們依次看下相關的知識。

數據綁定

Vue.js 使用了基於 HTML 的模版語法,容許開發者聲明式地將 DOM 綁定至底層 Vue 實例的數據。全部 Vue.js 的模板都是合法的 HTML ,因此能被遵循規範的瀏覽器和 HTML 解析器解析。下面是 Vue.js 數據綁定的相關語法:

  • 文本
    數據綁定最多見的形式就是使用 "Muestache" 語法(雙大括號),以下面的形式:
    html <span>Message: {{ msg }} </span>
    Muestache 標籤會被解析爲對應對象上的 msg 屬性值。當 msg 屬性發生改變以後,Muestache 標籤處解析的內容也會隨着更新。

    經過使用 v-once 指令,咱們能夠執行一次性解析,即數據改變時,解析的內容不會隨着更新。須要注意的是 v-once 會影響該節點上的全部數據綁定
    html <span v-once>This will never change: {{ msg }}</span>
  • Raw HTML
    不論屬性值是什麼內容,Muestache 標籤裏的內容都會被解析爲純文本。若是但願將綁定的值解析爲 HTML 格式,就須要使用 v-html 指令:
    html <div v-html="variable"></div>
  • 屬性值
    Mustache 語法不能用在 HTML 的屬性中,若是想爲屬性綁定變量,須要使用 v-bind 指令:
    html <div v-bind:id="dynamicId"></div>
    假設 dynamicId=1,那麼上面代碼就會被解析爲
    html <div id="1"></div>
    另外 v-bind 指令能夠被縮寫爲 :,因此咱們在程序中常常看到的是下面的語法形式:
    html <div :id="dynamicId"></div> <!-- 等價於 --> <div v-bind:id="dynamicId"></div>
  • 表達式
    對於全部的數據綁定, Vue.js 都提供了徹底的 JavaScript 表達式支持,以下面的形式:
    ```html
    // 加法
    {{ number + 1 }}

    // 三元表達式
    {{ ok ? 'YES' : 'NO' }}

    // JS 庫函數
    {{ message.split('').reverse().join('') }}

    // 指令中使用表達式


    ```

事件處理

經過使用 v-on 指令能夠監聽 DOM 事件來觸發 JS 處理函數,下面是一個完整的示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue Demo</title>
</head>
<body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
    <button v-on:click="increase">增長 1</button>
    <p>這個按鈕被點擊了 {{ counter }} 次。</p>
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: {
            counter: 0
        },
        methods: {
            increase: function() {
                this.counter++;
            }
        }
    })
</script>

</body>
</html>

一般狀況下,v-on 會被簡寫爲 @,因此咱們在程序中通常是看到下面的形式

<button @click="increase">增長 1</button>
<!-- 等價於 -->
<button v-on:click="increase">增長 1</button>

條件指令 v-if

經過 v-if 指令咱們能夠根據某些條件來決定是否渲染內容,以下面的形式

<h1 v-if="ok">Yes</h1>

咱們一般將 v-if 和 v-else 結合起來使用,以下所示:

<div v-if="Math.random() > 0.5">
    Now you see me
</div>
<div v-else>
      Now you don't
</div>

在 Vue 2.1.0 中新增了一個 v-else-if 指令,能夠進行鏈式判斷:

<div v-if="type === 'A'">
    A
</div>
<div v-else-if="type === 'B'">
      B
</div>
<div v-else-if="type === 'C'">
      C
</div>
<div v-else>
      Not A/B/C
</div>

循環指令 v-for

經過 v-for 指令,咱們能夠根據一組數據進行迭代渲染,下面是一個基本示例:

<ul id="example-1">
    <li v-for="item in items">
        {{ item.message }}
    </li>
</ul>
var example1 = new Vue({
    el: '#example-1',
    data: {
        items: [
              {message: 'Foo' },
              {message: 'Bar' }
        ]
    }
})

上面是一個簡單的對數組迭代的示例,咱們還能夠針對對象進行迭代,若是隻使用一個參數,就是針對對象的屬性值進行迭代:

<ul id="repeat-object" class="demo">
    <li v-for="value in object">
        {{ value }}
    </li>
</ul>

若是傳入第二個參數,就是針對對象的屬性值以及屬性名進行迭代,注意這裏二個參數表示的是屬性名,也就是 key

<div v-for="(value, key) in object">
    {{ key }} : {{ value }}
</div>

若是再傳入第三個參數,第三個參數就表示索引

<div v-for="(value, key, index) in object">
  {{ index }}. {{ key }} : {{ value }}
</div>

組件

組件是 Vue.js 最強大的功能之一。組件能夠擴展 HTML元素,封裝可重用的代碼。在咱們的程序中包含兩個組件:project 組件和 sidebar 組件,以下圖所示。這裏咱們主要介紹單文件組件的使用,即將組件用到 html、js 和 css 都寫在一個文件裏,每一個組件自成一個系統。

文件結構

單文件組件通常使用 ".vue" 做爲後綴名,通常的文件結構以下所示:

project.vue

<template>
    <div>
        {{ key }}
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                "key": "value"
            }
        },

        methods:  {
            demoMethod: function() {

            }
        }

    }
</script>

<style lang="less">
    @import "xxx.less";
</style>

export 將模塊輸出,default 代表使用文件名做爲模塊輸出名,這就相似於將模塊在系統中註冊一下,而後其餘模塊纔可用使用 import 引用該模塊。

而後咱們須要在主文件中註冊該組件:

index.js

import project from '../components/project/project.vue'
Vue.component("project", project);

當註冊完成以後,就能夠 html 中使用該組件了

index.html

<project></project>

生命週期

Vue 的要給組件會經歷 建立 -> 編譯 -> 掛載 -> 卸載 -> 銷燬 等一系列事件,這些事件發生的先後都會觸發一個相關的鉤子(hook)函數,經過這些鉤子函數,咱們能夠在事件發生的先後作一些操做,下面先看下官方給出的一個 Vue 對象的生命週期圖,其中紅框內標出的就是對應的鉤子函數

下面是關於這些鉤子函數的解釋:

hook 描述
beforeCreate 組件實例剛被建立,組件屬性計算以前
created 組件實例建立完成,屬性已綁定,可是 DOM 還未生成, $el 屬性還不存在
beforeMount 模板編譯/掛載以前
mounted 模板編譯/掛載以後
mounted 模板編譯/掛載以後(不保證組件已在 document 中)
beforeUpdate 組件更新以前
updated 組件更新以後
activated for keep-alive,組件被激活時調用
deactivated for keep-alive,組件被移除時調用
beforeDestory 組件銷燬前調用
destoryed 組件銷燬後調用

下面是鉤子函數的使用方法:

export default {
    created: function() {
        console.log("component created");
    },
    data {},
    methods: {}
}

父子組件通訊

父子組件通訊可使用 props down 和 events up 來描述,父組件經過 props 向下傳遞數據給子組件,子組件經過 events 給父組件發送消息,下面示意圖:

圖片來自 https://github.com/webplus/blog/issues/10

父組件向子組件傳遞數據

經過使用 props,父組件能夠把數據傳遞給子組件,這種傳遞是單向的,當父組件的屬性發生變化時,會傳遞給子組件,可是不會反過來。下面是一個示例

comp.vue

<template>
    <span>{{ message }}{{ shortMsg }}</span>
</template>

<script>
    export default {
        props: ["message", "shortMsg"],

    }
</script>

index.html

<div id="app">
    <!-- 在這裏將信息傳遞給子組件,:message 表示子組件中的變量名 -->
    <comp :message="hello" :short-msg = "hi"></comp>
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: {
            "hello": "Hello",
            "hi": "Hi"
        }

    })
</script>

在上面的流程中,父組件首先將要傳遞的數據綁定到子組件的屬性上,而後子組件在 props 中聲明與綁定屬性相同的變量名,就可使用該變量了,須要注意的一點是若是變量採用駝峯的命名方式,在綁定屬性時,就要將駝峯格式改成 - 鏈接的形式,若是上面所示 shortMsg -> short-msg

子組件向父組件通訊

若是子組件須要把信息傳遞給父組件,可使用自定義事件:

  1. 使用 $on(eventName) 監聽事件
  2. 使用 $emit(eventName) 觸發事件

下面是一個示例:

comp.vue

<script>
    export default {
        methods: {
            noticeParent: function() {
                // 事件名,傳輸值
                this.$emit('child_change', "value");
            }
        }
    }
</script>

index.html

<div id="app">
    <comp @child_change="childChange"></comp>
</div>
<script>
    var app = new Vue({
        el: '#app',
        methods: {
            childChange: function(msg) {
                console.log("child change", msg);
            }
        }
    });
</script>

在上面的代碼中,父組件經過 v-on 綁定了 child_chagne 事件,當 child_chagne 事件被觸發時候就會調用 childChange 方法。在子組件中能夠經過 $emit 觸發 child_change 事件。這裏須要注意的是事件名不用採用駝峯命名,也不要用 - 字符,可使用下劃線 _ 鏈接單詞。

Event Bus 通訊

Event Bus 通訊模式是一種更加通用的通訊方式,它既能夠用於父子組件也能夠用於非父子組件。它的原理就是使用一個空的 Vue 實例做爲中央事件總線,經過自定義事件的監聽和觸發,來完成通訊功能,下面是一個示意圖:

圖片來自 https://github.com/webplus/blog/issues/10

下面咱們來看一個具體的實例:

  • 首先定義一個空的 Vue 實例,做爲事件總線

    EventBus.js

    import Vue from 'vue'
    export default new Vue()
  • 在組件一中針對某個事件進行監聽

    comp1.vue

    <script>
    import eventBus from "EventBus.js"
    export default {
        created: function() {
            eventBus.$on("change", function() {
                console.log("change");
            })
        }
    }
    </script>
  • 在組件二中觸發相應事件完成通訊

    comp2.vue

    <script>
    import eventBus from "EventBus.js"
    export default {  
        methods: {
            notice: function() {
                this.$emit('change', "value");
            }
        }
    }
    </script>

ES6

本節摘自 ECMAScript 6 入門

與 ES5 相比,ES6 提供了更加完善的功能和語法,程序中咱們使用部分 ES6 語法,這裏作一個簡單的記錄,若是想要系統的學習 ES6,能夠參考下面的文章:

let

ES6 新增了 let 命令,用於聲明變量。使用 let 聲明的變量具備塊級做用域,因此在聲明變量時,應該使用 let,而不是 var。

{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

for of 循環

ES6 借鑑 C++、Java、C# 和 Python 語言,引入了for...of循環,做爲遍歷全部數據結構的統一的方法

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
  console.log(v); // red green blue
}

Set 和 Map

ES6 引入了 Set 和 Map 結構。下面是二者的具體介紹

Set

屬性

屬性 描述
Set.prototype.size 返回Set實例的成員總數。

方法

方法名 描述
add(value) 添加某個值,返回Set結構自己。
delete(value) 刪除某個值,返回一個布爾值,表示刪除是否成功。
has(value) 返回一個布爾值,表示該值是否爲Set的成員。
clear() 清除全部成員,沒有返回值。
   
keys() 返回鍵名的遍歷器
values() 返回鍵值的遍歷器
entries() 返回鍵值對的遍歷器
forEach() 使用回調函數遍歷每一個成員

使用示例:

const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

for (let i of s) {
  console.log(i);
}

Map

屬性

屬性 描述
Map.prototype.size 返回 Map 實例的成員總數。

方法

方法名 描述
set(key, value) set方法設置鍵名key對應的鍵值爲value,而後返回整個 Map 結構。若是key已經有值,則鍵值會被更新,不然就新生成該鍵。
get(key) 讀取 key 對應的鍵值,若是找不到 key,返回 undefined。
has(key) 返回一個布爾值,表示某個鍵是否在當前 Map 對象之中。
delete(key) 刪除某個鍵,返回true。若是刪除失敗,返回false。
clear() 清除全部成員,沒有返回值。
   
keys() 返回鍵名的遍歷器
values() 返回鍵值的遍歷器
entries() 返回全部成員的遍歷器
forEach() 遍歷 Map 的全部成員。

使用示例:

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

參考文章

相關文章
相關標籤/搜索