【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

参考文章

相关文章
相关标签/搜索