Vue进阶
1.1 安装vue-cli 脚手架
- Vue 提供了一个官方的 CLI,为单页面应用快速搭建 (SPA) 繁杂的脚手架。它为现代前端工作流提供了 batteries-included的构建设置。只需要几分钟的时间就可以运行起来并带有热重载、保存时 lint 校验,以及生产环境可用的构建版本。
- 需要安装 Node 环境,Node安装地址:http://nodejs.cn/download/,注意:安装 vue-cli 脚手架目前需要 4.0以上的 Node 版本。
1.1.1 vue-cli 全局安装
- 在本地终端根目录执行以下命令
npm i -g vue-cli //-g是全局安装,只要安装了在电脑任何地方都可以使用脚手架 - 安装完成以后 可以输入命令 :vue 回车,可以看到针对vue的命令行:
1.1.2 初始化项目
-
执行以下命令初始化项目
vue init webpack demo //demo 是你新建项目的名称 ,也是文件名称 -
执行之后将会自动初始化一个文件夹 :demo
-
默认的直接回车,Yes的直接输入y回车,No的输入n回车。最后再按回车进行初始化
-
成功以后就会创建完成一个项目模板
1.1.3 启动项目
- 执行以下命令启动项目
npm run dev
1.1.4 Vue 调试工具 Vue-devtools
1.1.4.1 在线安装
- 在线安装需要翻墙。地址:
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=zh-CN
1.1.4.2 本地安装
谷歌右上角菜单 - 更多工具 - 扩展程序 - 开发者模式打开 - 加载已解压的扩展程序 - 导入本地文件 - 成功以后显示以下图标
- 点击详细信息,勾选上允许访问文件网址,重启浏览器。F12打开控制台,有Vue一项表示安装成功
1.2 项目目录介绍
1.2.1 build
- webpack 配置相关
- 可以在此目录下的 webpack.base.conf 中的 resolve 中的 extentions 设置导入包时可以省略的文件的后缀名的类型
- 可以在此目录下的 webpack.base.conf 中的 resolve 中的 alias 设置文件路径别名,比如 src 就可以简写为 @
1.2.2 config
- 生产开发环境配置的参数
- 可以在此目录下的 index.js 中的 dev属性 port 修改端口号
- 可以在此目录下的 index.js 中的 dev属性 autoOpenBrowser 设置是否自动打开浏览器,true则自动打开
1.2.3 static
- static 中本来是用来放置第三方资源的,默认空文件是无法上传的,如果添加上 .gitkeep 则可以将空文件上传
1.2.4 node_modules
- 安装的第三方依赖
1.2.5 babelrc
- 将 es6 这种高级语法转为低级语法以便于浏览器去识别
1.2.6 src
- 做项目时写的源码,会被 webpack 进行进一步的处理打包
1.2.7 .editorconfig
- 编辑器使用的设置
1.2.8 .eslintignore
- 代码风格检查忽略文件
1.2.9 .eslintrc.js
- 代码风格检查
- rules 里面可以自定义规则
1.2.10 .gitignore
- 使用 git 提交项目的时候忽略的一些文件
1.3 编程式导航
- 借助于与router的实例 (this.$route)方法,通过编写代码来实现导航的切换。
- this.$route.back 回退一步
- this.$route.forward 前进一步
- this.$route.go 指定前进回退步数
- this.$route.push 导航到不同url,向history栈添加一个新的记录
- this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …() { this.route.back() //后退一步
},
forwardHandle () {
this.KaTeX parse error: Expected 'EOF', got '}' at position 27: …rd() //前进一步 }̲, goHandle (…route.go(3) //前进3步
this. route.go(0) //当前导航栏刷新
this.KaTeX parse error: Expected 'EOF', got '}' at position 30: …//超出浏览器记录无效 }̲, pushHandle…route.push(’/document’)
this. route.push({ name: ‘document’})
},
replaceHandle () {
this.$route.replace()
}
}
1.4 导航钩子函数
1.4.1 基本概念
- 导航发生变化时,导航钩子主要用来拦截导航,让他完成跳转或取消
1.4.1.1 执行钩子函数的 位置
- router 全局
- 单个路由
- 组件中
1.4.1.2 钩子函数
- router 实例上:beforeEach、afterEach (只要切换导航这两个钩子函数就会立即触发)
- 单个路由中:beforeEnter
- 组件内的钩子:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
1.4.1.3 钩子函数接收的参数
to:要进入的目标,路由对象,到哪里去
form:正要离开导航的路由对象,从哪里来
next:用来决定跳转或取消导航
1.4.2 beforeEach 全局钩子函数
- 在进入导航前执行的钩子函数
1.4.2.1 用法
- next 执行才能渲染目标导航
router.beforeEach ( (to, form, next) => {
next() //渲染导航
})
1.4.2.2 阻止导航以及重定向
- 假设 document 这个组件需要登录才能进入,我们就可以设置重定向。
- 目标路由
{
path: “/document”,
component: document,
meta: {
index: 3,
login: true
}
} - 重定向,to.meta.login 是 true 则跳转到 login 组件,false 则加载目标路由组件
router.beforeEach( (to,from,next) => {
to.meta.login ? next(’/login’) : next()
})
1.4.3 afterEach 全局钩子函数
- 在进入目标路由之后执行的钩子函数
- 目标路由
{
path: “/document”,
component: document,
meta: {
index: 3,
title: ‘document’
}
} - 跳转之后将目标导航的 title 改变
router.afterEach( (to,from,next) => {
if (to.meta.title) {
window.document.titile = to.meta.title // 如果有 title 就将 title 赋值给网页标题
} else {
window.document.titile = ‘Young’ // 如果没有指定 title 就设置为默认的 Young
}
})
1.4.4 beforeCreate 和 beforeRouterEnter
- beforeCreate :实例刚在内存中被创建出来,此时,还没有初始化好 data和 methods 属性。
- beforeRouterEnter :进入路由之前,组件级钩子函数。在 beforeCreate 之前创建,因此使用 this 拿不到 data 里面的数据,但是可以通过 next 中的回调函数来调用 data 中的数据。
beforeRouterEnter (to, from, next) {
next( (vm)=> {
vm.test = ‘改变了data中的数据!’
})
}
1.4.5 beforeRouteUpdate
- 导航更新钩子函数,组件级钩子函数。
beforeRouterUpdate (to, from, next) {
next()
}
1.4.6 beforeRouteLeave
- 导航离开组件的钩子函数,组件级钩子函数。
beforeRouterUpdate (to, from, next) {
next() //如果没有 next 则不会跳转到下一个链接
}
1.5 自定义属性 和 插件
1.5.1 自定义属性
- 在 main.js 里面可以通过原型给 vue 自定义属性,在其他组件可以通过this访问
Vue.prototype.$custom = “这是自定义属性” - 通常我们这样使用
var obj = {
install: function (Vue, options) {
Vue.prototype.$custom = ‘自定义属性’
}
}
Vue.use(obj,{a: 1})- 其中,Vue 就是我们的 Vue 构造函数,可以在里面使用原型绑定属性,options 是我们传进去的参数。
1.5.2 自定义插件
- 我们通过以上的方式可以封装一个自己的获取 localstorage 的插件
let local = {
save(key, value) {
localStorage.setItem(key,JSON.stringify(value))
},
fetch (key) {
return JSON.parse(localStorage.getItem(key) || {})
}
}
export default {
install: function (Vue) {
Vue.prototype.$local = local
}
}- 那么我们就可以在全局通过 $local 获取到这个local对象并调用下面的方法。
1.6 vuex
1.6.1 vuex 概念
- 专门为 Vue.js 应用程序开发的 状态管理模式,采用集中式存储管理应用的所有组件的状态,以相应的规则保证状态以一种可预测的方式发生变化。
- 状态:
- 组件内部状态:仅在一个组件内使用的状态(data字段)
- 应用级别状态:多个组件共用的状态
- 什么情况下使用 vuex 多个视图:
- 多个视图依赖同一状态
- 来自不同视图的行为需要变更同一状态
- 核心概念:
- store:类似容器,包含应用的大部分状态,一个页面只能有一个 store,状态存储是响应式的,唯一途径显式的提交 mutations
- state:包含所有应用级别状态的对象
- getters:在组件内部获取 store 中状态的函数
- mutations:唯一修改状态的时间回调函数(默认同步)
- actions:包含异步操作,提交mutation改变状态
- modules:将store分割成不同的模块
1.6.2 vuex 使用 和 定义
1.6.2.1 安装 和 导入
- 安装 vuex 模块
npm i vuex
1.6.2.2 导入 vuex 和 vue
-
src 目录下新建一个 store 文件夹,里面新建 index.js,在 index.js 中写入以下步骤因为要用到 vue 所以要导入 vue
import Vue from ‘vue’
import Vuex from ‘vuex’ -
作为插件使用
Vue.use(Vuex) -
定义容器,并且把实例暴露出去
let store = new Vuex.Store()
export default store
1.6.2.3 在 main.js 的 vue 中注入根实例
-
在 main.js 中导入 store 里面的 index.js,再在 Vue 中注入。
import store from ‘./store’new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })
1.6.3 vuex 基本操作
- 下面我们做一个小案例(简易计算器加减)来熟悉Vuex的基本使用。点击对应按钮可以使中间按钮数字变换。
1.6.3.1 提交mutation
-
vuex 状态存储是响应式的,唯一途径显式的提交 mutations
-
实现步骤
- 在 computed 计算属性中获取到 Vuex 中的 count 值(this.$store.state)
- 在 methods 中定义 add 和 reduce 两函数
- 将 Vuex 中 mutations 中定义的方法传过来,通过 commit 改变状态提交mutation,如果要传参,第二个参数传一个对象,在此对象中定义参数。
- 如果要传参,还可以直接在 commit 里面传一个对象,type 属性表示 mutaions 中定义的函数。
-
在页面中有一个两个 button 按钮,以及一个数。我们通过点击两个按钮使这个数增加或者减小。
<button type=“button” class=“btn btn-default” @click=“reduce”>-
{{num}}
<button type=“button” class=“btn btn-default” @click=“add”>+
-
store 中
- state 里面定义不同的状态(也就是需要改变的内容)
- mutations 里面定义状态函数,第一个参数为state, 可以通过 state 访问到状态里面的数据,第二个参数为组件的 commit 中传过来的参数
let store = new Vuex.Store({
state: {
count: 100
},
mutations: {
addIncrement(state, obj) {
state.count += obj.n;
},
reduceIncrement(state, obj) {
state.count -= obj.x;
}
}
});
-
组件
- 在这里,我们在 commit 传递了第二个参数,这个参数可以被 store 里面的函数给获取到,同样的是第二个参数。
computed: {
num () {
return this.KaTeX parse error: Expected 'EOF', got '}' at position 49: …state 里面的值 }̲ } methods:…store.commit(“addIncrement”, n: 5)
},
reduce () {
// 改变状态,提交 mutation
this.$store.commit({
type: ‘reduceIncrement’,
x: 2
})
}
}
- 在这里,我们在 commit 传递了第二个参数,这个参数可以被 store 里面的函数给获取到,同样的是第二个参数。
1.6.4 actions 异步操作
- 以上的例子我们希望1s以后再执行,mutation只能是同步的操作,由于1s以后再提交是异步的操作,如果在mutation使用同步操作,那么重要点击按钮就会触发mutation提交,此时的值是不会发生改变的。那么我们需要使用 actions,在 actions 中定义一个 addAction 函数,参数为 context 此对象中有 commit 函数,可以使提交 actions ,参数一:mutation 中的函数,参数二:可以传递自定义参数。
actions: {
/* 1s以后才执行 */
addAction (context) {
setTimeout(() => {
//改变状态,提交mutation
context.commit(“addIncrement”, { n: 5 });
}, 1000);
}
} - 然后在组件的add函数中通过 dispatch 触发 actions
add () {
this.$store.dispatch(“addAction”) //触发一个actions
} - 如果想要多个异步执行,第一个异步语句执行完毕再执行第二个。那么:
actions: {
/* 1s以后才执行 */
addAction (context) {
setTimeout(() => {
context.commit(“addIncrement”, {n: 5});
context.dispatch(“textAction”,{test: ‘测试’})
}, 1000);
},
textAction (context,obj) {
console.log(obj.test);
}
} - 在这里的话context 对象下面有两个方法,那么我们可以使用 es6 的解构赋值
actions: {
/* 1s以后才执行 */
addAction ({commit,dispatch}) {
setTimeout(() => {
commit(“addIncrement”, {n: 5});
dispatch(“textAction”,{test: ‘测试’})
}, 1000);
},
textAction (context,obj) {
console.log(obj.test);
}
}
1.6.5 vuex 流程
1.6.5.1 流程图
1.6.5.2 流程
- Vue Components:Vue 组件。HTML页面上,负责接收用户操作等交互行为,执行 dispatch 方法触发对应 actions 进行回应。
- dispatch:操作行为触发方法,是唯一能执行 actions 的方法。
- actions:操作行为处理模块。负责处理 Vue Components 接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台 API 请求的操作就在这个模块中进行,包括触发其他 actions 以及提交 mutation 的操作。该模块提供了 Promise 的封装,以支持 actions 的链式触发。
- commit:状态改变提交操作方法。对 mutation 进行提交,是唯一能执行 mutation 的方法。
- mutations:状态改变操作方法。是 Vuex 修改 state 的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些 hook 暴露出来,以进行 state 的监控等。
- state:页面状态管理容器对象。集中存储 Vue components 中 data 对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用 Vue 的细粒度数据响应机制来进行高效的状态更新。
- getters:state 对象读取方法。图中没有单独列出该模块,应该被包含在了 render 中,Vue Components 通过该方法读取全局 state 对象。
1.6.6 getters 计算状态
-
getters 是 vuex 用来处理不同的计算状态的。
-
现在我们有两个数字按钮,都可以进行加减,但是,对第二个按钮有限制条件,不能大于120。
<button type=“button” class=“btn btn-default” @click=“reduce”>-
{{num}}
{{num2}}
<button type=“button” class=“btn btn-default” @click=“add”>+
-
这时候我们就需要用到 getters,在 store 里面给 getters 属性, 里面定义一个函数 gettersCount,参数为 state,然后书写限制条件。返回一个值。
getters: {
gettersCount (state) {
return state.count < 120 ? 120 : state.count
}
} -
那么,在组件里面怎么去使用 getters 计算状态的 count 值呢。
computed: {
num () {
return this.KaTeX parse error: Expected 'EOF', got '}' at position 25: …te.count }̲, num2 ()…store.getters.gettersCount
}
}- 以上代码,同样的,getters 计算状态的值使用 this.$store.getters 获取到 getters 里面的值,那此时的值就有限制条件了。然后再渲染到 button 标签里面去。
1.6.7 vuex 辅助函数
- 辅助我们操作 vuex 的函数
- mapState 辅助操作 vuex 中 state 的值
- mapGetters 辅助操作 vuex 中 getters 里面的值
- mapAction 辅助操作 vuex 中 actions 的值
- mapMutations 辅助操作 vuex 中 mutation 提交
1.6.7.1 导入
- 分别导入四个 vuex 辅助函数
import {mapState, mapGetters, mapAction, mapMutations} from ‘vuex’
1.6.7.2 使用
- mapState
- 通过 …mapState([‘count’]) 将 count 扩展到 computed 上,这时候要渲染 count 值就需要在页面中使用 {{count}}
computed: {
…mapState([‘count’])
} - 如果想要使用其他名字
computed: {
other () {},
…mapState({
num: ‘count’ //这里的 count 为 vuex 中的 state 中的 count
})
}
- mapGetters
- 同样的。将 getters 里面的值扩展到 computed 上
computed: {
other () {},
…mapGetters([‘gettersCount’]), //这里的 gettersCount 为 vuex 中的 getters 中的函数
…mapState([‘count’])
}
3.mapAction
- action 异步
actions: {
addAction(context) {}
}
methods: {
…mapActions([‘addAction’]) //这里的 addAction 为 vuex 中的 actions 中的函数
}
- mapMutation
- 改变状态时,提交 mutation 相当于 commit
mutations: {
addIncrement(state, obj) {
state.count += obj.n;
},
reduceIncrement(state, obj) {
state.count -= obj.x;
}
}
…mapMutations({
reduce: ‘reduceIncrement’ //这里的 reduceIncrement 为 mutations 中的函数
}) - 这里要注意的是,如果在mutations里面传有参数,则需要在事件函数里面传参数过去
<button type=“button” class=“btn btn-default” @click=“reduce({x: 2})”>-
1.6.8 Vuex的module模块化
- 当不同的组件的Vuex多了以后就容易混乱,所以我们会模块化。将不同功能的模块给提出来。
let slectModule = {
state: {
title: ‘’,
},
mutations: {
},
actions: {
},
modules: {} // 子模块还可以再嵌套子模块
} - 容器,只能有一个。
- 这时候,需要通过 modules 将子模块 selectModule 注入到 Vuex 中
let store = new Vuex.Store({
state: {
count: 100,
},
mutations: {
},
actions: {
},
modules: {
selectModule //将子模块 selectModule 注入到 Vuex 中
}
}) - 那么,在要获取数据的时候
获取根Vuex: this. store.state.selectModule.title