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 <871673791@qq.com> --> `作者,回车 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 移动到里面
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', // 将 router-link 的类名统一修改 linkActiveClass: 'active' }) // 4.将 router 对象传入到 vue 实例 export default router
// main.js import App from './App.vue' import router from './router' new Vue({ el: '#app', router, render: h => h(App) })
2.3 配置简单路由
- <router-link></router-link>
是vue-router
里已经配置好的组件,最终被渲染成a
标签 - <router-view></router-view>
占位,组件渲染位置
// 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'
})
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view/>
</div>
2.4 router-link 属性
-
to 属性: 对应注册的 path 路径
-
tag 属性: 将 router-link 渲染成对应的标签
<router-link tag ="button"></router-link>
-
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' } }
第二步:配置路由
{ path: '/user/:userId', component: User }
第三步:渲染用户信息
<div class="user">我是用户{{ userId }}</div> computed: { userId() { return this.$route.params.userId } }
我是用户lisi
匹配模式 | 匹配路径 | $route.params |
---|---|---|
/users/:username | /users/eduardo | { username: ‘eduardo’ } |
/users/:username/posts/:postId | /users/eduardo/posts/123 | { username: ‘eduardo’, postId: ‘123’ } |
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 编程式导航
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
注意:如果提供了 path,params 会被忽略。需要提供路由的 name 或手写完整的带有参数的 path :
const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user
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 } ]
or
const routes = [ { path: '/home', component: () => import('../components/home') } ]
2.7 命名视图
可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。
<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):
const routes = [
{
path: '/',
components: {
default: Home,
// LeftSidebar: LeftSidebar 的缩写
LeftSidebar,
// 它们与 `<router-view>` 上的 `name` 属性匹配
RightSidebar,
}
}
]
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`
}
}
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
}
})
import { mapState } from 'vuex'
// 映射 this.count 为 this.$store.state.count
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
映射到局部计算属性
// store.js
export default {
state: {
cartList: []
},
getters: {
cartCount(state) {
return state.cartList.length
}
}
}
import { mapGetters } from 'vuex'
// 把 `this.cartCount` 映射为 `this.$store.getters.cartCount`
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
// 提交一个行为到 mutations
this.$store.commit('increment')
参数被称为mutation
的载荷(payload),也可以是一个对象
普通传参
this.$store.commit('incrementCount', 5)
// mutations.js
incrementCount(state, count) {
state.count += 5
}
传对象参数
// App.vue
this.$store.commit({
type: 'incrementCount',
count: 5
})
// 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) {
...
}
}
mapMutations 映射
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
3.3.4 actions
处理异步操作,可以接收两个参数,conrext
表示上下文,payload
表示接收数据,可以是一个对象
// store > index.js
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
// App.vue
this.$store.dispatch('increment')
mapActions 映射
// actions.js
export default {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
import { mapActions } from 'vuex'
methods: {
...mapActions(['actionA']),
// 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 的状态
// 访问模块中的 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.3.6 调用其他模块的方法和属性
打印 action 的第一个参数
commit 用于调用 mutation,当前模块和其他模块
dispatch 用于调用 action,当前模块和其他模块
getters 用于获取当前模块 getter
state 用于获取当前模块 state
rootState 用于获取其它模块 state
rootGetters 用于获取其他模块 getter
在一个模块的actions中调用其他模块的actions
dispatch('vip/get', {}, {root: true})
参数一:是其他模块的 actions 路径,。
参数二:是传给 actions 的数据, 如果不需要传数据, 也必须预留,
参数三:是配置选项, 申明这个 acitons 不是当前模块的
在一个模块需要使用其他模块的getters
rootGetters['vip/get']
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() // 发射事件 this.$bus.$emit('imgLoad') // 注册监听事件 this.$bus.$on('imgLoad',this.itemImgListener) // 移除监听事件 this.$bus.$off('imgLoad',this.itemImgListener)
4、环境变量配置
-
设置开发环境的环境变量
-
在根目录创建一个
.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)
注意:设置完成后,需要重新启动脚手架
-