Vue2
认识
概念
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统
<div id="app">
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
v-
attribute 被称为指令。指令带有前缀 v-
,以表示它们是 Vue 提供的特殊 attribute。
它们会在渲染的 DOM 上应用特殊的响应式行为
- JavaScript 框架
- 简化 Dom 操作
- 响应式数据驱动
核心
- 数据绑定
- 事件绑定
- 用户输入获取
- 组件定义和使用
引入
CDN
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
Vue CLI
安装
npm install -g @vue/cli
升级
npm update -g @vue/cli
创建项目
vue create project-name
安装必要工具
npm install
开启本地端口
npm run serve
npm run serve -- --port 5000
基础
Vue 实例
var app = new Vue({
el:"#app",
data:{
message:"Hello World",
array:[],
obj:{},
}
})
- el:挂载点,
- Vue 会管理 el 选项命中的元素及其内部的后代元素
- 可以挂载到双标签上,不能使用 HTML 和 BODY
- data:数据对象
- Vue 中用到的数据定义在 data 中
- data 中可以写复杂类型的数据
- 渲染复杂类型数据时,遵守 js 的语法即可
Vue 指令
Vue 指令指的是,以 v- 开头的一组特殊语法
v-text
设置标签的文本值 (textContent)
支持在内部写表达式,如 v-text="message + '!'"
v-text 指令无论内容是什么,只会解析为文本
var app = new Vue({
el: "#app",
data: {
message: "Hello"
}
})
默认写法:在文本标签中添加 v-text
属性值,会替换全部内容
<div id="app">
<h2 v-text="message"></h2>
</div>
差值表达式 {{ }} 可以替换指定内容
<div id='app'>
<p>{{message + '!'}}</p>
</div>
v-html
设置标签的 innerHTML
若内容中有 html 结构,会被解析为标签,如 content:"<a href='#'>Hello World</a>"
辨析:解析文本使用 v-text,需要解析 html 结构使用 v-html
var app = new Vue({
el:"#app",
data:{
content:"Hello World"
}
})
<div id="app">
<p v-html="content"></p>
</div>
v-on
为元素绑定事件
-
事件名不需要写 on
-
指令可以简写为 @
-
绑定的方法定义在 methods 属性中
-
方法内部通过 this 关键字可以访问定义在 data 中数据
var app = new Vue({
el: "#app",
methods: {
doIt: function () {
// ...
}
}
})
<div id="app">
<input type="button" value="bind1" v-on:click="doIt">
<input type="button" value="bind2" v-on:monseenter="doIt">
<input type="button" value="bind3" v-on:dblclick="doIt">
<input type="button" value="bind4" @dblclick="doIt">
</div>
例:计数器
<div id="app">
<input type='button' value='-' v-on:click='sub'>
<span>{{value}}</span>
<input type='button' value='+' v-on:click='add'>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
value: 1
},
methods: {
sub: function () {
this.value -= 1;
},
add: function () {
this.value += 1;
}
},
})
</script>
修饰符
事件的后面跟上 .修饰符
可以对事件进行限制
.stop
- 调用event.stopPropagation()
。.prevent
- 调用event.preventDefault()
。.capture
- 添加事件侦听器时使用 capture 模式。.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调。.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调。.native
- 监听组件根元素的原生事件。.once
- 只触发一次回调。.left
- (2.2.0) 只当点击鼠标左键时触发。.right
- (2.2.0) 只当点击鼠标右键时触发。.middle
- (2.2.0) 只当点击鼠标中键时触发。.passive
- (2.3.0) 以{ passive: true }
模式添加侦听器
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 动态事件 (2.6.0+) -->
<button v-on:[event]="doThis"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 动态事件缩写 (2.6.0+) -->
<button @[event]="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter">
<!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
v-show
根据表达值的真假,切换元素的显示和隐藏
- 原理是修改元素的 display,实现显示隐藏
- 指令后面的内容,最终都会解析为布尔值
var app = new Vue({
el:"#app",
data:{
isShow:false,
age:16
}
})
<div id="app">
<img src="地址" v-show="true">
<img src="地址" v-show="isShow">
<img src="地址" v-show="age>=18">
</div>
v-if
根据表达值的真假,切换元素的显示和隐藏 (操纵 dom 元素)
- 本质是通过操纵 dom 元素来切换显示状态
- 表达式的值为 true,元素存在于 dom 树中,为 false,从 dom 树中移除
- 辨析:频繁的切换使用 v-show,偶尔切换使用 v-if,前者的切换消耗小
var app = new Vue({
el:"#app",
data:{
isShow:false,
}
})
<div id="app">
<p v-if="true">我是一个p标签</p>
<p v-if="isShow">我是一个p标签</p>
<p v-if="表达式">我是一个p标签</p>
</div>
v-bind
设置元素的属性:v-bind:属性名=表达式
- 可以用
:
简写 - 需要动态的增删 class 建议使用对象的方式
var app = new Vue({
el:"#app",
data:{
imgSrc:"picture-path",
imgTitle:"text",
isActive:false
}
})
<div id="app">
<img v-bind:src= "imgSrc" >
<img v-bind:title="imgTitle+'!'">
<img v-bind:class="isActive?'active':''">
<img v-bind:class="{active:isActive}">
</div>
v-for
根据数据生成列表结构
- 语法:
(item, index) in array
- item 和 index 可以结合其他指令一起使用
- 数组长度的更新会同步到页面上,是响应式的
var app = new Vue({
el: "#app",
data: {
arr: ['a', 'b', 'c', 'd', 'e'],
objArr: [
{ name: 'Mike' },
{ name: 'Jack' }
]
}
})
<div id="app">
<ul>
<li v-for="(item,index) in arr" :title='item'>
{{index}} -> {{ item }}
</li>
<li v-for="item in objArr">
{{ item.name }}
</li>
</ul>
</div>
v-model
获取和设置表单元素的值 (双向数据绑定)
- 绑定的数据会和表单元素值相关联
<div id="app">
<input type="text" v-model="message" />
</div>
组件间的数据传递
父子组件之间的数据传递可以使用 props 或者 $emit 等方式
父传子
使用 props
子组件部分

父组件部分

子传父
通过事件传递数据
子组件部分

父组件部分

一些原理
计算属性 computed 中的 getter
在 Vue 中,computed 的属性可以被视为是 data 一样,可以读取和设值,因此在 computed 中可以分成 getter(读取) 和 setter(设值),一般情况下是没有 setter 的,computed 预设只有 getter ,也就是只能读取, 不能改变设值。
vue.js 计算属性默认只有 getter,因为是默认值所以我们也常常省略不写,如下代码:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
其实 computed 里的代码完整的写法应该 是:
computed: {
fullName: {
get(){
return this.firstName + ' ' + this.lastName
}
}
}
axios
介绍
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
引入
使用 npm:
$ npm install axios
使用 bower:
$ bower install axios
使用 cdn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
样例
axios({
method: 'GET',
url: url,
})
.then(res => {console.log(res)})
.catch(err => {console.log(err)})
axios.post('/user', {
firstName: 'Mike',
lastName: 'Allen'
}).then( res => {
console.info(res)
}).catch( e => {
console.info(e)
})
使用
axios(config)
// 发送 POST 请求
axios({
method: 'post',
url: 'www.google.com',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
axios(url[, config])
// 发送 GET 请求(默认的方法)
axios('/user/12345');
别名
axios.get(url[, config])
axios.post(url[, data[, config]])
配置项
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // 默认是 get
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `transformRequest` 允许在向服务器发送前,修改请求数据
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
transformRequest: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `headers` 是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// `paramsSerializer` 是一个负责 `params` 序列化的函数
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时 ,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // 默认的
// `adapter` 允许自定义处理请求,以使测试更轻松
// 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
adapter: function (config) {
/* ... */
},
// `auth` 表示应该使用 HTTP 基础验证,并提供凭据
// 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // 默认的
// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` 是承载 xsrf token 的值的 HTTP 头的名称
xsrfHeaderName: 'X-XSRF-TOKEN', // 默认的
// `onUploadProgress` 允许为上传处理进度事件
onUploadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// `onDownloadProgress` 允许为下载处理进度事件
onDownloadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// `maxContentLength` 定义允许的响应内容的最大尺寸
maxContentLength: 2000,
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
validateStatus: function (status) {
return status >= 200 && status < 300; // 默认的
},
// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5, // 默认的
// `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
// `keepAlive` 默认没有启用
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// 'proxy' 定义代理服务器的主机名称和端口
// `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
// 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
proxy: {
host: '127.0.0.1',
port: 9000,
auth: : {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// `cancelToken` 指定用于取消请求的 cancel token
// (查看后面的 Cancellation 这节了解更多)
cancelToken: new CancelToken(function (cancel) {
})
}
工程化目录结构
脚手架
架构
main.js
程序入口
App.vue
主视图
<template>
<div id="app">
<div>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</div>
</template>
<script>
export default {};
</script>
<style lang="scss">
</style>
结构:<template>
脚本: <script>
样式:<style>
页面中放入一个路由视图容器:
<router-view></router-view>
引用路由链接:
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
router
路由配置例
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
}
]
用 Vue.js + Vue Router 创建单页应用
使用 Vue.js ,我们已经可以通过组合组件来组成应用程序;当你要把 Vue Router 添加进来,我们需要做的是,将组件 (components) 映射到路由 (routes),然后告诉 Vue Router 在哪里渲染它们。
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
单文件组件

Vuex
认识
vuex 是一个专门为 vue.js 应用程序开发的状态管理模式。
核心思想:把组件的共享状态抽取出来,以一个全局单例模式进行管理
五类对象
- state:存储状态(变量)在组件中使用
$store.state.foo
- getters:对数据获取之前的再次编译,可以理解为 state 的 computed 属性。在组件中使用
$store.getters.fun()
- mutations:修改状态,并且是同步的。在组件中使用
$store.commit('funcName',params)
。它和组件中的自定义事件类似。 - actions:异步操作。在组件中使用是
$store.dispath('funcName',params)
- modules:store 的子模块,为了开发大型项目,方便状态管理而使用的。
简单配置
main.js
Vuex 提供了一个从根组件向所有子组件,以 store
选项的方式 “注入” 该 store 的机制
//main.js内部对store.js的配置
import store from '"@/store/store.js'
//具体地址具体路径
new Vue({
el: '#app',
store, //将store暴露出来
template: '<App></App>',
components: { App }
});
store/index.js
import Vue from 'vue'; //首先引入vue
import Vuex from 'vuex'; //引入vuex
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// state 类似 data
//这里面写入数据
},
getters:{
// getters 类似 computed
// 在这里面写个方法
},
mutations:{
// mutations 类似methods
// 写方法对数据做出更改(同步操作)
},
actions:{
// actions 类似methods
// 写方法对数据做出更改(异步操作)
}
})
或者
mport Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {}
const getters = {}
const mutations = {}
const actions = {}
const store = new Vuex.Store({
state,
getters,
mutations,
actions
})
export default store;
但更建议的做法是:在单独的文件里写好每个组件的状态,最后统一在 index.js
中引入
项目结构
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
对象
state
state 中存放状态对象
const state = {
todos: [
{ id: 1, title: "Wake up", done: false },
{ id: 2, title: "Learn", done: false },
]
}
以形似 $store.state.todos
或 $store.state.todo.todos
(如果此状态分模块单独存放的话)调用
getters
getters 里存放着从 state 中派生出来的一些状态,类似于 computed,所以 getters 里的状态都具有相应的依赖值,而且以函数的形式进行定义
const getters = {
doneTodos: state => {
return state.tasks.filter(task => task.done)
},
doneTodosNum: (state, getters) => {
return getters.doneTodos.length
}
}
注意,getter 也可以接受其他 getter 作为第二个参数
通过属性访问
getter 会暴露为 store.getters
对象,可以以属性的形式访问这些值,如 $store.getters.doneTodoNum
,不分组件
通过方法访问
也可以通过让 getter 返回一个函数,来实现 给 getter 传参。在对 store 里的数组进行查询时非常有用
getTodoById: state => id => {
return state.todos.find(todo => todo.id === id)
}
// 调用时
$store.getters.getTodoById(2)
注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果
mutations
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
const mutations = {
addTask(state, newTaskTitle) {
console.log('add: ' + newTaskTitle);
let newTask = {
id: Date.now(),
title: newTaskTitle,
done: false,
};
state.tasks.push(newTask);
newTaskTitle = "";
},
}
要唤醒一个 mutation handler,需要以相应的 type 调用 store.commit 方法,如 $store.commit('mutations','Sleep')
,不分组件
载荷
传入的额外参数称为载荷(payload)
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
// 调用时
store.commit('increment', {
amount: 10
})
type 属性
提交 mutation 的另一种方式是直接使用包含 type
属性的对象
store.commit({
type: 'increment',
amount: 10
})