1、Vue-Cli
1.1 常用命令
1.1.1 安装
-
安装 vue-cli3 以上版本
npm i @vue/cli -g
-
拉取 vue-cli2 模板
npm i @vue/cli-init
上面安装好之后,就既可以使用 vue-cli2 ,也可以使用 vue-cli3
1.1.2 创建项目
-
vue-cli3 以上版本
vue create my-project
--> my-project 为项目名Please pick a preset: //配置选择 > default //默认 > Manually select features //手动选择 * #空格选择配置项 Where do you prefer placing...? //配置项保存选择 > In dedicated config files //保存为独立文件 * > In package.json //保存到package.json Save this as a preset for futrue projects? (y/N) //是否保存配置项,下次创建项目时,第一项就有三个选项
- 配置项选择
Babel : 将ES6编译成ES5 TypeScript: javascript类型的超集 Progressive Web App (PWA) Support: 支持渐进式的网页应用程序 Router:vue-router Vuex: 状态管理 CSS Pre-processors: CSS预处理 Linter / Formatter: 开发规范 Unit Testing: 单元测试 E2E Testing: 端到端测试
-
vue-cli2
vue init webpack my-project
--> my-project 为项目名Project name my-project --> `项目名,回车 Project description A Vue.js project --> `描述信息,回车 Author lxc <[email protected]> --> `作者,回车 Vue build <Use arrow keys> > `Runtime + Compiler --> 代码中可以有 template,回车 > `Runtime-Only --> 项目文件比上面的轻,一般项目开发使用这个 Install vue-router? (Y/n) --> `是否安装路由 Use ESLint to lint your code? (Y/n) --> `是否启用代码规范 > `Standard //标准规范 > `Airbnb //爱彼迎规范 > `none //自定义规范 to invert selection > >(*) Lint on save // 保存就检测 (我一般选择它) ( ) Lint and fix on commit // fix和commit检查 Set up unit tests (Y/n) --> `是否单元测试,n Setup e2e tests with Nightwatch? (Y/n) --> `是否端到端测试,n >>>>> 安装完毕
1.2 eslint 关闭
有时候,配置了 eslint 代码规范,反而给我们写代码造成了困扰
config>index.js>useEslint:false
1.3 vue-cli 3 与 2 区别
- 3 是基于 webpack 4 打造,2 是基于webpack 3
- 3 的设计原则是 “0配置”,移除根目录下的 build 和 config 等目录
- 3 提供了 vue ui 命令,提供了可视化配置
- 3 移除了 static 文件夹,新增了 public 文件夹,并将 index.html 移动到里面
1.4 修改配置
在 vue-cli 3中,webpack 相关配置都隐藏到了 node_modules 下 @vue 目录中,可以通过以下几种方案修改配置
- 命令
vue ui
,打开图形化界面,找到项目目录修改 - 创建
vue.config.js
文件,修改独有配置
2、Vue-Router
路由就是通过互联的网络把信息从源地址传输到目的地地址的活动
核心就是:改变 url,但是页面不进行整体刷新
2.1 方案
-
改变 url 的 hash
-
url 的 hash 也就是锚点(#),本质上是改变window.location的href属性
location.hash = '/foo' // url/#/foo
-
-
HTML5的history模式:pushState
history.pushState({ },'','foo') // url/foo // 这种可以保留历史记录,调用 history.back() 方法可以后退
history.replaceState({ },'','foo') // 这种是替换当前页面,不能前进后退
2.2 安装和使用
-
安装
npm i vue-router
-
在模块化工程中使用它
因为是一个插件,所以可以通过Vue.use()来安装路由功能
第一步:导入路由对象,并且调用 Vue.use(VueRouter)
第二步:创建路由实例,并且传入路由映射配置
第三步:在Vue实例中挂载创建的路由实例
router>index.js //1.导入路由对象 import Router from 'vue-router' import Vue from 'vue' //2.通过 Vue.use(插件),安装插件 Vue.use(Router) //3.创建 Router 对象 const routes = [ ] const router = new Router({ //配置路由和组件 routes, //将默认hash模式调成history模式 mode: 'history' }) index.js const router = new Router({ //配置路由和组件 routes, //将默认hash模式调成history模式 mode: 'history', //将 router-link 的类名统一修改 linkActiveClass: 'active' }) //4.将 router 对象传入到 vue 实例 export default router
main.js import router from './router' new Vue({ el: '#app', router, render: h => h(App) })
2.3 配置简单路由
-
是
vue-router
里已经配置好的组件,最终被渲染成a
标签 -
占位,组件渲染位置
index.js
import Home from '../components/home'
import About from '../components/about'
//注册路由
const routes = [
{
path: '',
//重定向,第一次显示首页
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
const router = new Router({
routes,
mode: 'history'
})
App.vue
//使用
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view/>
</div>
通过代码的方式修改路由 vue-router
<button @click="btnClick"></button>
btnClick() {
this.$router.push('/home')
//this.$router.replace('/home')
}
2.4 router-link 属性
-
to 属性: 对应注册的 path 路径
-
tag 属性: 将 router-link 渲染成对应的标签
<router-link tag ="button"></router-link> //渲染成 button 标签
-
replace 属性:换成 history. replaceState 替换当前页面,没有前进后退
-
active-class 属性:选中的标签默认有两个 class 类名
router-link-exact-active 和 router-link-active
-
如果要改,直接赋值
active-class="active"
,项目中不建议改 -
统一修改
-
2.5 动态路由
根据不同的用户信息显示不同的页面,传递参数主要有两种类型:
2.5.1 params
的类型
- 配置路由格式:/router/:id
- 传递的方式:在
path
后面跟上对应的值 - 传递后形成的路径:/router/123,/router/abc
第一步:注册组件
<router-link :to="'/user/'+userId">用户</router-link> data() { return { userId: 'lisi' } }
第二步:配置路由
index.js { path: '/user/:userId', component: User }
第三步:渲染用户信息
<div class="user">我是用户{ {userId}}</div> computed: { userId() { return this.$route.params.userId } }
我是用户lisi
2.5.2 query
的类型
- 配置路由格式:/router,也就是普通配置
- 传递的方式:对象中使用
query
的key
作为传递方式 - 传递后形成的路径:/router?id=123,/router?id=abc
第一步:注册组件
<router-link :to="{path:'/profile',query:{name:'zhangsan'}}">档案</router-link>
第二步:配置路由
{ path: '/profile', component: Profile }
第三步:渲染用户信息
<div> <h2>{ {$route.query.name}}</h2> </div>
zhangsan
2.5.3 其他标签
<button @click="userClick">用户</button>
<button @click="profileClick">档案</button>
userClick() {
this.$router.push('/user' + this.userId)
},
profileClick() {
this.$router.push({
path: '/profile',
query: {
name: 'zhangsan'
}
})
}
2.6 路由的懒加载(分离打包)
- 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载
- 不同的组件分割成不同的代码块,当路由被访问时再加载对应的组件
方式
一:结合Vue的异步组件和Webpack的代码分析
const Home = resolve => { require.ensure(['../components/home.vue'],() => { resolve(require('../components/home.vue')) }) }
二:AMD写法
const Home = resolve => require(['../components/home.vue'],resolve)
三:ES6写法
const Home = () => import('../components/home.vue') * const routes = [ { path: '/home', component: Home } ]
const routes = [ { path: '/home', component: () => import('../components/home') } ]
2.7 路由的嵌套
希望通过 /home/news
访问一些内容
index.js
const HomeNews = () => import('../components/homeNews')
const HomeMessage = () => import('../components/homeMessage')
{
path: '/home',
component: Home,
children: [
{
path: '',
redirect: 'news'
},
{
path: 'news',
component: HomeNews
},
{
path: 'message',
component: HomeMessage
}
]
}
home.vue
<div class="home">我是首页,哈哈</div>
<router-link to="/home/news" tag="button">新闻</router-link>
<router-link to="/home/message" tag="button">消息</router-link>
<router-view></router-view>
2.8 导航守卫
在一个SPA应用中,监听路由的跳转过程,做一些操作,比如更改网页的 title
标题
2.8.1 全局守卫
-
beforeEach
:前置守卫(guard),路由跳转之前回调,必须要调用next()
函数to
:即将要进入的目标路由from
:即将要离开的路由对象next
:调用该方法,才能进入下一个钩子
-
beforeResolve
: 解析守卫,和router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用 -
afterEach
:后置钩子(hook),路由跳转之后回调,不需要再调用next()
函数router.afterEach((to,from) => { ... })
index.js
第一步:路由文件定义函数
router.beforeEach((to,from,next) => { document.title = to.matched[0].meta.title next() }) //或者 router.afterEach((to,from) => { document.title = to.matched[0].meta.title })
第二步:定义元数据(描述数据)
{ path: '/about', component: About, meta: { title: '关于' }, }
2.8.2 路由独享的守卫
-
直接在某个路由配置上定义
beforeEnter
守卫,参数跟beforeEach
一样to
:即将要进入的目标路由from
:即将要离开的路由对象next
:调用该方法,才能进入下一个钩子
{ path: '/about', component: About, meta: { title: '关于' }, beforeEnter: (to,from,next) => { console.log('beforeEnter') next() } }
2.8.3 组件内的守卫
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
2.9 URL 的 hash 和 H5 的 history
改变 url,但是页面不进行整体刷新,vue-router 会监听路径的改变触发相应的回调函数,默认是 hash 模式
-
URL 的 hash,本质是是改变
location
的hash
属性localhost:8080 location.hash = 'foo' localhost:8080/#/foo
-
H5 的 history
localhost:8080 history.pushState({},'','foo') localhost:8080/foo
3、Vuex
一个专门为 Vue.js 应用程序开发的状态管理模式
- 如用户的登录状态、用户信息
- 如商品的收藏、购物车的物品等等
3.1 作用流程
组件引用(Render)vuex
中的state
,发出(Dispatch)一个行为(Actions),提交(Commit)到 Mutations
,再来改state
Devtools
可以记录修改的状态,但是必须是通过Mutations
修改的,可以不经过Actions
直接修改,但是必须是同步操作,Devtools
只能跟踪同步,异步操作只能通过Actions
,再提交到Mutations
就是同步了
3.2 安装使用
-
安装
npm i vuex
-
使用
// store 文件夹 index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: { } }) export default store // main.js import store from './store' new Vue({ el: '#app', store, render: h => h(App) })
3.3 Vuex 五个核心概念
如果状态信息是保存到多个 Store 对象中,那么之后的管理和维护都会非常困难,所以 vuex 使用了单一状态树管理应用层级的全部状态
响应式原理
Store 对象中的属性都会被加入到响应式系统中,而响应式系统会监听属性的变化,当属性发生变化时,通知所有界面用到该属性的地方发生刷新
- 所有响应式属性必须是先在
store
定义好的- 后面追加的属性如果要响应式,须通过 Vue.set() 方法追加
3.3.1 state
单一状态树(数据源),放状态相关的信息,在项目中任何位置可以通过$store.state
访问其里面的属性
mapState
辅助函数
// store/index.js
const store = new Vuex.Store({
state: {
count: 10
}
})
// App.vue
<div>{
{count}}</div>
import { mapState } from 'vuex'
computed: {
...mapState({
count: 'count' // 这里可以重命名
})
// 或者
...mapGetters(['count']} // 这里不能重命名
}
3.3.2 getters
类似计算属性,有时候需要获取 state
编译后的状态
const store = new Vuex.Store({
state: {
count: 10
},
/**
* getters 里的方法第一个参数指向 state 状态
* getters 里的方法第二个参数指向 getters 状态
*/
getters: {
more(state) {
return state.count * 10
},
moreTwo(state, getters) {
return getters.more * 10
},
//可以直接返回一个函数让用户操作
backCount(state) {
return count => {
state.count = count
}
}
}
})
// App.vue
<p>{
{
$store.getters.more}}</p>
<p>{
{
$store.getters.moreTwo}}</p>
// 此处把 count 改成了 20
<p>{
{
$store.getters.backCount(20)}}</p>
// 200
// 2000
// 20
mapGetters 映射
将store
中的getters
映射到局部计算属性
// getters.js
export default {
cartCount(state) {
return state.cartList.length
}
}
// Cart.vue
<div slot="center">购物车({
{
cartCount}})</div>
import {
mapGetters } from 'vuex'
computed: {
...mapGetters({
cartCount: 'cartCount' // 这里可以重命名
})
// 或者
...mapGetters(['cartCount']} // 这里不能重命名
}
3.3.3 mutations
更新 store
状态的唯一方式:提交(commit)mutations
里面的方法必须是同步函数,同步操作可以在组件内直接 commit
到mutations
普通提交
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
//mutations 里的方法参数 state 指向 state 状态
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
}
})
//3.导出
export default store
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<p>{
{
$store.stare.count}}<p>
methods: {
addition() {
//提交一个行为到 mutations
this.$store.commit('increment')
},
subtraction() {
this.$store.commit('decrement')
}
}
参数被称为mutation
的载荷(payload),也可以是一个对象
普通传参
// App.vue
addCount(count) {
this.$store.commit('incrementCount',count)
}
// mutations.js
incrementCount(state,count) {
return state.count += 5
}
传对象参数
// App.vue
addCount(count) {
this.$store.commit({
type: 'incrementCount',
count
})
}
// mutations.js
incrementCount(state,payload) {
return state.count += payload.count
}
类型常量
避免mutation
中方法名出错,官方推荐使用类型常量
// store/mutation-types.js
export const INCREMENT = 'increment'
// index.js
import {
INCREMENT } from './mutation-types'
mutations: {
[INCREMENT](state) {
}
}
// App.vue
addition() {
this.$store.commit('increment')
}
3.3.4 actions
处理异步操作,可以接收两个参数,conrext
表示上下文,payload
表示接收数据,可以是一个对象
// index.js
mutations: {
updateInfo(state) {
return state.data = 2
}
},
actions: {
//context:上下文,payload:接收的参数,可以是一个对象
aUpdateInfo(context,payload) {
return new Promise((resolve,reject) => {
setTimeout(() => {
context.commit('updateInfo')
console.log(payload);
resolve('异步返回')
},1000)
})
}
}
// App.vue
revise() {
//dispatch 一个行为到 action,传入一个参数
this.$store
.dispatch('aUpdateInfo','修改完成')
.then(res => {
console.log(res);
})
}
mapActions 映射
// actions.js
export default {
addCart(context, payload) {
return new Promise((reslove, reject) => {
let oldProduct = context.state.cartList.find(
(item) => item.iid === payload.iid
)
// 查找数组中是否有相同的商品
if (oldProduct) {
context.commit(ADD_COUNTER, oldProduct)
reslove('当前商品数量+1')
} else {
payload.count = 1
context.commit(ADD_TO_CART, payload)
reslove('添加商品成功')
}
})
}
}
// Detail.vue
import {
mapActions } from 'vuex'
methods: {
...mapActions(['addCart']),
this.addCart(product).then(res => {
console.log(res)
})
// 等同于
// this.$store.dispatch('addCart', product).then(res => console.log(res))
}
3.3.5 modules
- Vue 使用单一状态树,那么意味着很多状态都会交给 Vuex 来管理
- 当应用变得复杂时,stote 对象就有可能变得相当臃肿
- 为了解决这个问题,可以将 store 分割成模块(Module),而每个模块都拥有自己的
state
、getters
、mutations
、actions
用法
const moduleA = {
state: {
name: 'zhangsan'
},
getters: {
fullname(state) {
return state.name + '111'
},
fullname2(state,getters,rootState) {
return getters.fullname + rootState.name
}
},
mutations: {
},
actions: {
}
}
const moduleB = {
state: {
},
getters: {
},
mutations: {
},
actions: {
}
}
const stote = new Vuex.Stote({
state:{
name: 'lisi'
},
//默认会把 a,b 对象放入 state 中
modules: {
a: moduleA,
b: moduleB
}
})
stote.state.a // moduleA 的状态
stote.state.b // moduleB 的状态
App.vue
//访问模块中的 state
<p>{
{
$store.state.a.name}}</p> //zhangsan
//访问模块中的 getters ,直接访问,先去内部找,没有再去模块中找
<p>{
{
$store.getters.fullname}}</p> //zhangsan111
//模块中的 getters 第三个参数指向实例中的 state
<p>{
{
$store.getters.fullname2}}</p> //zhangsan111lisi
//actions 和 mutations 用法也是直接用
3.4 事件总线
组件发射一个事件this.$bus.$emit('aaa',参数)
,其他组件都可以通过this.$bus.$on('aaa',callback)
监听,通过this.$bus.$off('aaa',callback)
取消监事件函数(一定要传函数,不然会全局取消事件)
-
需要在 main.js 里添加一个原型属性
$bus
// main.js Vue.prototype.$bus = new Vue() // GoodsItem.vue imgLoad() { this.$bus.$emit('imgLoad') } // Home.vue mounted() { this.itemImgListener = () => { ... } this.$bus.$on('imgLoad',this.itemImgListener) }, deactivated() { this.$bus.$off('imgLoad',this.itemImgListener) },
4、axios
4.1 功能特点
- 在浏览器中发送 XMLHttpRequest 请求
- 在 node.js 中发送 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求和响应数据
- …
4.2 请求方式
axios(config)
axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.post(url[,data[,config]])
axios.put(url[,data[,config]])
axios.patch(url[,data[,config]])
4.3 安装使用
4.3.1 安装
npm i axios
main.js
import axios from 'axios'
4.3.2 使用
get 请求
//method 不写默认是 get 请求
axios({
url: '', //地址
params: {
//get请求传参,或者直接拼在地址后也可以
}
}).then(res => {
//请求成功,操作数据
console.log(res)
})
axios.get(url, {
params: {
}
})
.then(res => {
console.log(res);
})
post 请求
axios.post(url, {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(res => {
console.log(res);
})
// 发送 POST 请求
axios({
method: 'post',
url: '',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
并发请求
axios.all([axios(),axios()])
.then(res => {
console.log(res) //返回的数据放在一个数组里
})
//或者使用这种方法直接拿结果
axios.all([axios(),axios()])
.then(axios.spread((res1,res2) => {
console.log(res1)
console.log(res2)
}))
4.3.3 配置项
url
:请求地址method
:请求类型baseURL
:请求根路径transformRequest:(data) => {}
:请求前的数据处理transformResponse:(data) => {}
:请求后的数据处理headers:{'x-Requested-With'}
:自定义请求头params
:url 查询对象,适用于 get 请求data
:作为请求主体被发送的数据,只适用于’PUT’, ‘POST’, 和 ‘PATCH’timeout
:超时设置
4.4 创建实例与模块封装
4.4.1 创建实例
如果接口有多个服务器,那么就可以通过创建实例来配置选项
const instance1 = axios.create({
baseURL: 域名, //配置这个之后的 url 都只需输路径
timeout: 5000//请求超过5s报错
})
instance1({
url: 路径,
methos: 'get',
params: {
}
})
4.4.2 模块封装
避免第三方库与组件发生强藕性,建议封装个 axios 使用
-
创建文件夹 network ,下面创建个 request.js
import axios from "axios"; export function request(config) { const instance = axios.create({ baseURL: "http://152.136.185.210:8000/api/z8", timeout: 5000, }); return instance(config); }
-
其他文件引入就可以直接使用
import {request} from './network/request' request({ url: 路径, methos: 'get', params: { } })
4.5 拦截器
4.5.1 请求拦截
请求成功拦截,请求失败拦截
适用场景:
- 比如
config
中的一些信息不符合服务器的要求,需要添加一些信息 - 每次发送请求时,希望在界面显示一个正在请求的动画图标
- 某些网络请求(比如登录(token)),必须携带一些特殊的信息
import axios from 'axios'
export function request(config) {
//1.创建axios实例
const instance = axios.create({
baseURL: 域名 //配置这个之后的 url 都只需输路径
timeout: 5000//请求超过5s报错
})
//2.axios 拦截器
instance.interceptors.request.use(config => {
console.log(config)
//注意:拦截之后一定要把结果返回出去
return config
},err => {
console.log(err)
})
//3.发送真正的网络请求
return instance(config)
}
4.5.2 响应拦截
响应成功拦截,响应失败拦截
服务器响应回来的数据,axios会自动加一些东西,但是我们只需要data里的东西,所以可以做一层拦截
import axios from 'axios'
export function request(config) {
//1.创建axios实例
const instance = axios.create({
baseURL: 域名 //配置这个之后的 url 都只需输路径
timeout: 5000//请求超过5s报错
})
//2.axios 拦截器
//请求拦截
instance.interceptors.request.use(config => {
//console.log(config)
//注意:拦截之后一定要把结果返回出去
return config
},err => {
console.log(err)
})
//响应拦截
instance.interceptors.response.use(res => {
//注意:拦截之后一定要把结果返回出去
return res.data
},err => {
console.log(err)
})
//3.发送真正的网络请求
return instance(config)
}
5、环境变量配置
-
设置开发环境的环境变量
-
在根目录创建一个
.env.development
的文件 -
设置开发环境变量 (注意点:以
VUE_APP_变量名
开头)VUE_APP_BASEURL = http://127.0.0.1/heimamm/public
-
访问:
process.env.VUE_APP_变量名
alert(process.env.VUE_APP_BASEURL)
-
-
设置生产环境的环境变量
-
在根目录创建一个
.env.production
的文件 -
设置开发环境变量 (注意点:以
VUE_APP_变量名
开头)VUE_APP_BASEURL = http://autumnfish.cn/heimamm/public
-
访问:
process.env.VUE_APP_变量名
alert(process.env.VUE_APP_BASEURL)
注意:设置完成后,需要重新启动脚手架
-