Vue2 复习
Vue版本:
- V2: https://cn.vuejs.org/v2/guide/installation.html
- V3: https://v3.cn.vuejs.org/guide/introduction.html
Vue CLI:
他山之石:
Vue 核心
- Vue 模版语法
- 插值语法
- 指令语法
- 数据绑定
- 单向绑定
- 双向绑定
- el 与 data 的两种写法
- MVVM 模型
- 数据代理
- 事件处理
- 绑定监听
- 事件修饰符
- 按键修饰符
- 计算属性
- 监视属性
- 绑定样式
class
绑定style
绑定
- 条件渲染
v-if 和 v-else
v-show
- 列表渲染
v-for
- 收集表单数据
- 过滤器
- 内置指令
- 局部指令
- 全局指令
- 自定义指令
- ⭐ 生命周期
Vue 组件化编程
- 非单文件组件
- 基本使用
- 注意点
- 组件的嵌套
- ⭐ VueComponent,Vue 组件实例,与 Vue 很像,不包括
el
属性和data
函数写法 - ⭐ 一个重要的内置关系:
VueComponent.prototype.__proto__ === Vue.prototype
- 单文件组件
- 定义入口组件
创建组件(VueComponent
)
const hello = Vue.extend({
template:`
<div>
<h2>你好啊!{{name}}</h2>
</div>
`,
data(){
return {
name:'Tom'
}
}
})
定义全局组件
// 定义名为 todo-item 的组件
Vue.component('todo-item', {
props: ['todo'],
template: '<li>这是个待办项</li>',
})
Vue.component('hello', hello)
使用局部组件
new Vue({
el:'#root',
data:{
msg:'你好啊!'
},
//第二步:注册组件(局部注册)
components:{
school,
student
}
})
VueComponent.prototype.__proto__ === Vue.prototype
对象具有属性
__proto__
,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。JS中一切皆对象。其中方法(Function)这个特殊的对象,除了和其他对象一样有上述
__proto__
属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做 constructor,这个属性包含了一个指针,指回原构造函数。
vc.$on
:先从 vc
中找 $on
,找不到则去 VueComponent 的原型对象
中找;若找不到则去 VueComponent.__proto__
中找;一直找到 Object 的原型对象
其 __proto_
为 null 不能继续找下去
Vue 脚手架
-
脚手架文件结构
-
vue.config.js
配置文件,配置参考这里 -
⭐
ref
属性(id
的替代者) -
⭐
props
配置项- 父组件向子组件传递数据、函数等
-
mixin
混入,共用配置复用 -
⭐插件,用于增强 Vue
- 插件需要包含
install
方法;Vue.use
使用插件
- 插件需要包含
-
scoped
限定样式范围 -
⭐ Todo-list 案例:通用组件化编码流程
-
自定义事件
- 原生元素的事件与组件的事件
- 方式一:通过父组件给子组件传递函数类型的props实现子给父传递数据
- 方式二:
@xxx="eFun"
和this.$emit('xxx', yyy)
- 方式三:使用
ref
和this.$ref.xxx.$on('xxx', fun)
绑定自定义事件,更加灵活 - 自定义事件的解绑:
this.$off()
或this.$destroy()
-
全局事件总线,任意组件间通信
- 利用
VueComponent.prototype.__proto__ === Vue.prototype
机制保证所有的 vm 和 vc 都可见 - 注册:
mounted() { Vue.prototype.$bus = this }
、使用:this.$bus.on()
、this.$¥$bus.$emit()
、this.$bus.$off()
- 利用
-
消息订阅与发布
- 使用
pubsub-js
,与【全局事件总线】有异曲同工之妙
- 使用
-
this.$nexttick(function(){})
在下一次 Dom 节点更新完之后执行其指定的回调【是一个生命周期钩子】setTimeout(callback, 0)
也可实现功能:将定时任务放在任务队列尾部
-
动画效果和过渡效果
- 使用 Vue 提供的
trasition
标签。多个元素都需要过渡则需要transition-group
- 第三方动画库
- 使用 Vue 提供的
-
代理服务器
- 使用 axios 向后端请求数据,由于同源策略的限制(浏览器的同源策略)会产生跨域问题。使用代理服务器的方式解决(代理服务器与后端使用 http 进行交互不受同源策略的限制)
- 配置
vue.config.js
- 注意:如果访问的资源本身就有,就不会走代理服务器找
- 高级:根据路径前缀配置多个代理方式,使用
pathRewrite: {'^/api':''}
重写路径
-
vue-resource
请求库,需要使用Vue.use
进行安装,使用类似 axiosthis.$http.get()
。官方已不再维护,不推荐使用 -
slot 插槽
- 默认插槽
slot
- 具名插槽:多个同级插槽共存。使用
name
指定slot
,使用slot=“x”
指定插槽;使用template
去掉外层div
,换v-slot:x
的方式指定插槽 - 作用域插槽:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
- 默认插槽
使用脚手架:
npm i -g @vue/cli
(原名为 vue-cli
,已废弃)
vue --version
查看版本
vue ui
可视化创建脚手架项目(或传统方式在终端通过 vue create <app-name>
创建)
Vuex
- 将多组件共享的状态放在vuex中,与【全局事件总线】不同的是将数据放在一个独立的位置
-
搭建 Vuex 环境
-
actions
:处理业务逻辑;mutations
:直接操作数据。如果没有业务逻辑可以直接使用$store.commit()
处理数据 -
开发工具的使用(开发者工具与 mutations 交互)
-
actions
中的复用 -
getters
定义数据加工逻辑,通过 getters 暴露出去,类似computed
-
mapState, mapActions, mapMutation, mapGetters
简化computed
和methods
中的代码量 -
多组件之间共享数据
-
使用命名空间
-
actions
中的方法发送请求,模拟复杂操作
npm i vuex@3.6.2
import Vue from 'vue'
import vuex from 'vuex'
import axios from 'axios'
Vue.use(vuex)
export default new vuex.Store({
actions: {
getToken(context, value) {
return new Promise((resolve) => {
axios.get(`https://madewith.cn/?page=1&_ajax=1&value=${value}`).then(
(/*resp*/) => {
// console.log('#', resp.data)
let user = {
id: '11241',
name: 'Jack',
age: 18,
}
context.commit('SET_USER', user)
resolve(user)
},
(err) => {
console.log(err.message)
}
)
})
},
},
mutations: {
SET_USER(state, user_obj) {
this.state.user = user_obj
},
},
getters: {
//处理 state 后进行暴露
},
state: {
user: undefined,
},
})
action 复用
const actions = {
business(context, value) {
// dispatch 到另一个 action
context.dispatch('common', value)
},
common(context, value) {
//...
},
b2(context, value) {
// 提交修改
context.commit('B2', value)
}
}
const mutations = {
B2(state, value) {
state.sum += value
}
}
模块化写法:
const countAbout = {
namespaced: true, // 开启命名空间
state: {x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){ return state.sum * 10 }
}
}
const personAbout = {
namespaced: true, // 开启命名空间
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
模块化方式下的不通读取方式对比:https://www.yuque.com/cessstudy/kak11d/dkrrce#4d8705de
// 方式一:自己直接读取
this.$store.state.countAbout.sum
// 方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
//方式一:自己直接读取
this.$store.getters['countAbout/bigSum']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('personAbout',{addPWang:'addPersonWang',addPServer:'addPersonServer'})
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('personAbout',{addp:'ADD_PERSON'}),
mapXX 简化写法怎么用:
Vue-router
一个插件库,专门来实现 SPA
应用。单机页面中的导航连接不会刷新页面,只会做页面的局部更新。
什么是路由?一个路由就是一组映射关系(key/value)。key 为路径,value 可能是 function 或 component
- 前端路由:value 是 component ,用于展示页面内容
- 后端路由:value 是 function,用于处理客户端提交的请求。具体工作流程是服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
2022.2.7 vue-router 默认版本是 4,只能在 vue3 中用;vue-router 3 才能在 vue2 中用
npm i vue-router
-
基本使用:使用
router-link
实现路由切换,使用router-view
实现组件展示 -
注意:被切换的组件被销毁了;每个组件都有自己的
$route
,整个应用只有一个 router,可通过this.$router
获取 -
嵌套路由(多级路由);配置
children
不要写/
,在router-link
中要指定完整路径 -
query
参数- 传递方式:
to
属性传递字符串或对象 - 通过
$route.query.xxx
获取参数
- 传递方式:
:to = `/a/b/c?id=${m.id}&name={m.name}`"
:to = "{
name: 'xxx', // name or path
query: {
id: m.id,
name: m.name
}
}"
- 命名路由。简化路由跳转的配置
:to="{ name: 'route-name' }"
params
参数
:to = "`/a/b/c/${m.id}/${m.name}`"
// 对象的写法
// 路由切换的 to 属性,如果用 params 则不能用 path,必须用 name
:to = "{
name: 'xxx',
params: {
}
}"
// 路由配置处要声明接收params
path: "c/:id/:name"
//接受参数
this.$route.params.id
props
属性。三种写法。使路由组件在接受参数时直接使用 props 中的变量,不从 $route 中拿,后者太繁琐
// 1: 传递固定数据,用的少
{
name: 'nm',
path: 'pt',
props: { a:1, b:'hello' }
}
// 2:在组件中声明 props 后就可以使用了
{
// 将 params 参数以 props 的形式传递给组件
props:true
}
// 3:值为函数,返回一个对象,以 props 的形式传递给组件
// 最强大的写法
{
// props: function() {}
props($route) { // 回调函数,可以拿到 query。可以用【解构赋值】简化
return {a:1, title:'666', }
}
}
//使用解构赋值简化
{
props({query:{id, title}}) { // 解构赋值连续写法,取出 $route.query.id 和 $route.query.title
return {id, title}
}
}
-
浏览历史:
push
模式:追加历史记录,属于压栈操作,router-link
默认模式replace
模式:替换掉当前记录,替换栈顶元素,需要为router-link
添加replace
属性
-
编程式路由导航,编码控制路由
pushShow(m) {
this.$router.push({ // .replace
name: 'xiangqing',
query: {
id: m.id,
title: m.title
}
})
}
this.$router.back()
this.$router.forward()
this.$router.go(n) //. n>0 前进n步,n<0 后退n步
- 缓存路由组件。切换路由时不销毁路由视图,不指定
include
时默认缓存所有组件;include
中指定组件名
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
// :include="['AompA','CompB']"
- 两个新的生命周期钩子:路由组件独有的两个勾子,用于捕获路由组件的激活失活状态。
export defalut {
data() {
return {}
},
// 组件被激活
actived() {
},
// 组件失活(离开该组件)
deactived() {
}
}
- 全局路由守卫
const router = new VueRouter({
routes:[]
})
// 前置路由守卫
// 初始化的时候被调用、每次切换路由之前被调用
router.beforeEach((to, from, next)=>{
console.log(to, from)
if (to.meta.isAuth) { // 配置的路由元信息
if (localStorage.getItem('token')) {
next() // 放行
} else {
alert('token 不存在')
}
}
})
router.afterEach((to, from)=>{
console.log(to, from)
document.title = to.meta.title || '@vue/cli系统'
})
export default router
- 路由元信息
meta
: 存放自定义的路由配置信息
new VueRoute({
routes: [
{
name: 'x',
path: '/x',
component: 'X',
meta: { isAuth:true }
}
]
})
- 独享路由守卫:某一个路由所独享的。只有前置,没有后置路由守卫
new VueRouter({
routes: [
{
name:'x',
path:'/x',
component:X,
beforeEnter((to,from,next)=>{
})
}
]
})
- 组件内路由守卫(与全局路由守卫不一样)
export default {
name: 'About',
data() {
return{}
},
//通过路由规则【进入该组件】时被调用
beforeRouteEnter(to, from, next) {
},
//通过路由规则【离开该组件】时被调用
afterRouteLeave(to, from, next) {
}
}
history
模式和hash
模式hash
模式,带#
号,不美观,#
后边内容称为 hash 值,不会发送给服务器,兼容性比较好。history
模式,地址干净美观,兼容性比hash
略差,应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。
new VueRouter({
mode: 'history', // hash localhost:8080/#/xxx
routes: []
})
npm i vue-router@3.5.1
import Vue from 'vue'
import VueRouter from 'vue-router'
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
import store from '@/store'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/',
redirect: '/v1',
name: 'home',
component: () => import('@/components/HomeComp'),
children: [
{
path: 'v1',
name: 'view1',
component: () => import('@/views/ViewPage1'),
},
{
path: 'v2',
name: 'view2',
component: () => import('@/views/ViewPage2'),
},
{
path: 'v3',
name: 'view3',
component: () => import('@/views/ViewPage3'),
},
],
},
{
path: '/login',
name: 'login',
component: () => import('@/views/LoginPage'),
},
],
})
const whiteList = ['/v1', '/v2', '/login']
router.beforeEach((to, from, next) => {
Nprogress.start()
if (whiteList.indexOf(to.path) !== -1) {
Nprogress.done()
next()
} else {
if (!localStorage.getItem('token')) {
Nprogress.done()
// 重定向到登录页面
next(`/login?redirect=${to.path}`)
} else {
// 每次刷新都从后台拿用户数据
store
.dispatch('getToken', localStorage.getItem('token'))
.then((/* resp */) => {
// console.log('@', resp)
Nprogress.done()
next()
})
}
}
})
export default router
UI库
-
蚂蚁团队:Antd for vue
-
饿了吗团队:ElementUI