一直没有找到一个合适的展现我的项目的模板,因此本身动手使用 Vue 写了一个。该模板基于 Markdown 文件进行配置,只须要按必定规则编写 Markdown 文件,而后使用一个 在线工具 转为 JSON 文件便可。下面是该项目的在线地址和源码。本文主要记录一下项目中用到的相关知识。css
程序最终的效果以下图所示:vue
整个项目只包含两个组件:项目介绍 和 侧边导航,逻辑比较简单,十分适合入门。node
这里咱们使用 Gulp 和 Webpack 用做项目构建工具。初次使用 Gulp 和 Webpack 可能不太适应,由于它们的配置可能让你看的一头雾水。不过不用担忧,这两个毕竟只是一个工具,在初始时没有必要特别的了解它们的工做原理,只要能运行起来就能够。等到使用了一段时间以后,天然而然的就知道该如何配置了。这里主要记录一下项目中使用的配置,若是想要系统的学习如何使用这两个工具,能够参考下面的文章: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 中主要配置了两个任务: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 进行资源打包的工做,就是说将各类资源(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 会将 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 文件,可是一开始时老是没法压缩文件,后来查阅了一下资料,大概是由于下面几个缘由:
npm install mishoo/UglifyJS2#harmony --save
plugins: [ new webpack.LoaderOptionsPlugin({ minimize: true }) ]
若是按上面的修改了仍是不能压缩文件,能够试着将 node_modules 删除,而后从新安装依赖。
本部分主要记录一下程序中用到的 Vue 语法,若是想要系统的学习一下 Vue.js,能够参考下面的文章:
咱们首先来看一个最简单的 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>
v-html
指令:html <div v-html="variable"></div>
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 指令咱们能够根据某些条件来决定是否渲染内容,以下面的形式
<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
指令,咱们能够根据一组数据进行迭代渲染,下面是一个基本示例:
<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
。
若是子组件须要把信息传递给父组件,可使用自定义事件:
下面是一个示例:
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 通讯模式是一种更加通用的通讯方式,它既能够用于父子组件也能够用于非父子组件。它的原理就是使用一个空的 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>
本节摘自 ECMAScript 6 入门
与 ES5 相比,ES6 提供了更加完善的功能和语法,程序中咱们使用部分 ES6 语法,这里作一个简单的记录,若是想要系统的学习 ES6,能够参考下面的文章:
ES6 新增了 let 命令,用于声明变量。使用 let 声明的变量具备块级做用域,因此在声明变量时,应该使用 let,而不是 var。
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1
ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for...of循环,做为遍历全部数据结构的统一的方法
const arr = ['red', 'green', 'blue']; for(let v of arr) { console.log(v); // red green blue }
ES6 引入了 Set 和 Map 结构。下面是二者的具体介绍
属性
属性 | 描述 |
---|---|
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.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