前置知识
- 函数默认参数
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function fn( {title='a'} = {title:'b'} ){
console.log(title)
}
fn() // b 无参数title默认为b
fn({}) // a 有空对象的参数默认title为a
fn({ name: 'hello' }) // a 无title相当于空对象
fn({ title:'hello' }) // hello 有参数title为传的值
fn(a) // 报错
- 模块化
写法一
export var a = 'a' // a.js
import {a} from 'a.js' // b.js
写法二
var a = 'a'
export {a} // a.js
import {a} from 'a.js' // b.js
写法三
export default function() { // a.js
console.log('6')
}
import getNum from 'a.js' // b.js
getNum()
- 类与继承
- 构造函数
class Persion {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log(`Hi ${this.name}, I am ${this.age}`)
}
}
等价于
function Persion(name, age) {
this.name = name;
this.age = age;
}
Persion.prototype.sayHi = function(){
console.log(`Hi ${this.name}, I am ${this.age}`)
}
var p = new Persion('Lee', 16)
- 静态方法
class EventCenter{
static fire(){}
static on(){}
}
等同于
function EventCenter(){}
EventCenter.fire = function(){}
EventCenter.on = function(){}
- 继承
class Persion {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log(`Hi ${this.name}, I am ${this.age}`)
}
}
class Student extends Persion {
constructor(name, age, score){
super(name, age);
this.score = score;
}
sayScore(){
console.log(`Hi ${this.name}, I am ${this.age}, I get ${ths.score}`)
}
}
- 字符串拼接:
var data = 'vue'
var a = `This is ${data}`
// This is vue
项目需求
UI设计:
创建账号--登录--笔记本列表--笔记详情--回收站
交互分析:
- 假设用户未登录,当访问网站时,默认跳转到【登录页面】
- 用户此时可输入账号密码进行登录。也可点击注册账号跳转到【注册页面】进行注册
- 登录成功后默认跳转到【笔记本列表页面】, 用户可新建笔记本、删除笔记本、修改笔记本标题
- 用户点击某条笔记本,会跳转到 【笔记页面】,【笔记】页面展示当前笔记本下的笔记列表以及其中一个笔记的详情。用户可在笔记本列表顶部切换笔记本,切换笔记本后会自动展示该笔记本下的笔记列表,并自动打开列表下的第一条笔记
- 当用户点击回收站时,会跳转到【回收站】页面,回收站页面里有所有临时删除的笔记。用户可彻底删除笔记,也可恢复笔记到原笔记本
- 当用户点击注销时,回到登录页面
接口约定:前后端接口文档规范
vue-cli项目搭建
cd yourdist
npm install -g @vue/cli-init // 拉取2.x版本
vue init webpack vue-evernote-client
cd vue-evernote-client
npm install
npm run dev
npm run build
环境
npm install --save axios@0.18.0
npm install less --save-dev
生产和开发环境 baseURL 切换
原理:
将存放baseURL的文件抽离出来,在 /build/mock.config.js 文件下判断当前环境:运行npm run dev
则为开发环境,写入mock路径;运行npm run build
则为生产环境,写入后端提供的路径。这样就实现不同环境请求不同的URL。
实现:
环境判断:
const fs = require('fs')
const path = require('path')
const mockBaseURL = '//localhost:3000'
const realBaseURL = '//note-server.hunger-valley.com'
exports.config = function ({ isDev = true } = { isDev: true }) {
let fileTxt = `
module.exports = {
baseURL: '${isDev ? mockBaseURL : realBaseURL}'
}
`
fs.writeFileSync(path.join(__dirname, '../src/helpers/configURL.js'), fileTxt)
}
开发环境:
require('./mock.config').config({ isDev: true })
生产环境:
require('./mock.config').config({ isDev: false })
封装请求:
import axios from 'axios'
import baseURLConfig from './configURL'
axios.defaults.headers.post['Content-Type']
axios.defaults.baseURL = baseURLConfig.baseURL
axios.defaults.withCredentials = true // 允许跨域
export default function request(url, type = 'GET', data = {}) {
return new Promise((resolve, reject) => {
let option = {
url,
method: type,
validityState(status) {
return (status >= 200 && status < 300) || status === 400
}
}
if (type.toLowerCase() === 'get') {
option.params = data
} else {
option.data = data
}
axios(option).then(res => {
if (res.status === 200) {
resolve(res.data)
} else {
console.error(res.data)
reject(res.data)
}
}).catch(err => {
console.error({ msg: '网络异常' })
reject({ msg: '网络异常' })
})
})
}
运行结果:
运行npm run dev
,写入了开发环境的baseURL
router跳转的两种方法
声明式(模板)
<router-link :to="">
编程式(js)
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
$router已经全局注册到组件中,因此
this.$router.push({ path: '/login' })
等同于
import Router from '@/router/index'
Router.push({ path: '/login' })
PS:
为什么要加 $
呢?在Vue.js中,通过以 $
开头的属性或方法,可以区分用户自定义的属性和Vue框架提供的属性/方法。Vue框架内部的一些特殊属性和方法都以 $
开头,这有助于避免与用户定义的属性和方法发生命名冲突。
vue2组间通信
-
父子组件:父组件传一个props相当于向子组件进行一次通信;子组件调用事件 emit 一个 click 相当于向父组件进行一次通信
-
爷孙组件:
- 使用两次父子组件间他通信
- 使用依赖注入
provide
+inject
来通信
-
任意组件:eventBus = new Vue 或 vuex
eventBus.$on 和 eventBus.$emit收发通知
需求举例:由于请求用户名写在created导致在登录后不自动更新头像内的名字;要实现登陆后自动捕获并显示用户名首个字符
实现原理:Vue允许创建多个根节点,所以全局创建一个新的Vue,将被监听的方法$on()
挂载到上面,在对应的地方调用$emit()
// 全局创建根节点
import Vue from 'vue'
export default new Vue()
// 监听获取用户名的方法
Bus.$on('userInfo', user => {
this.username = user.username
})
// 登陆成功后调用该方法
onLogin(){
...
Bus.$emit('userInfo', {
username: this.login.username
})
}
生命周期
computed
<template>
<span :title="user.username">{{ slug }}</span>
</template>
<script>
export default {
data() {
return {
username: 'Valley',
}
},
computed: {
slug() {
return this.username.charAt(0)
}
}
}
</script>
浏览器直接调试封装好的方法
import Auth from '@/apis/auth'
import NotebookList from '@/apis/notebooks'
window.NotebookList = NotebookList // 直接在浏览器调试
阻止默认事件传播:点击只触发某个标签的事件而不再事件冒泡
登录功能
登陆有错时显示错误信息:v-bind + css
<template>
<div class="login">
<p v-bind:class="{ error: register.isError }">{{ register.notice }}</p>
</div>
</template>
<script>
export default {
data: {
return {
login: {
isError: false
}
}
}
}
</script>
<style>
.login {
error {color: red}
}
登录跳转:在created时期获取用户信息isLogin
判断是否登录,未登录则跳转到登录页;登陆后跳转到对应页面
created)() {
Auth.getInfo()
.then(res => {
if(!res.isLogin){
this.$router.push({path: '/login'})
}
}
}
封装
axios封装
import axios from 'axios'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
axios.defaults.baseURL = 'http://xxx.com'
axios.defaults.withCredentials = true
export default function request(url, type = 'GET', data = {}) {
return new Promise((resolve, reject) => {
let option = {
url,
method: type,
validateStatus(status) {
return (status >= 200 && status < 300 || status === 400)
}
}
if (type.toLowerCase() === 'get') {
option.params = data
} else {
option.data = data
}
axios(option).then(res => {
if (res.status === 200) {
resolve(res.data)
} else {
reject(res.data)
}
}).catch(err => {
reject({ msg: '网络异常' })
})
})
}
接口封装
import request from '@/helpers/request'
const URL = {
REGISTER: '/auth/register',
LOGIN: '/auth/login',
LOGOUT: '/auth/logout',
GET_INFO: '/auth'
}
export default {
register({ username, password }) {
return request(URL.REGISTER, 'GET', { username, password })
},
login({ username, password }) {
return request(URL.LOGIN, 'POST', { username, password })
},
logout() {
return request(URL.LOGOUT)
},
getInfo() {
return request(URL.GET_INFO)
}
}
计算距离现在的时间差
使用vuex
Vuex 是 Vue.js 应用程序的状态管理模式。它集中式地存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。以下是 Vuex 的基本用法介绍:
1. 安装 Vuex
在使用 Vuex 之前,你需要先安装它。你可以使用 npm 或 yarn 来安装:
npm install vuex --save
# or
yarn add vuex
2. 创建 Store
Vuex 的核心是 store(仓库)。一个 store 实例单例管理着所有组件的状态。
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
actions: {
increment(context) {
context.commit('increment');
}
},
getters: {
count: state => state.count
}
});
3. 在 Vue 实例中注册 Store
// main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store';
new Vue({
render: h => h(App),
store
}).$mount('#app');
4. 在组件中使用 Vuex
你可以在组件中通过 this.$store
访问 store 的状态和方法。
// Component.vue
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
}
},
methods: {
increment() {
this.$store.commit('increment');
}
}
};
</script>
5. Vuex 核心概念
State
Vuex 使用单一状态树——是一个对象包含全部应用层级状态,仅一个状态树对象就包含了全部状态,在开发中将它保存在一个对象中。
const state = {
count: 0
};
Mutations
要改变 store 中的状态,唯一途径是提交 mutation。这是一个同步事务。
const mutations = {
increment(state) {
state.count++;
}
};
Actions
Actions 类似于 mutations,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
const actions = {
increment(context) {
context.commit('increment');
}
};
Getters
类似于组件的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
const getters = {
count: state => state.count
};
6. 模块化 Store
当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了应对这种情况,Vuex 允许将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter,甚至是嵌套子模块。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
};
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
};
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
});
通过模块化管理,可以使代码结构更加清晰,维护起来也更方便。