Vue2 复习

Vue版本:

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

image-20220330134439250

对象具有属性 __proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。

JS中一切皆对象。其中方法(Function)这个特殊的对象,除了和其他对象一样有上述__proto__属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做 constructor,这个属性包含了一个指针,指回原构造函数。

img

参考 https://zhuanlan.zhihu.com/p/92894937

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)
    • 方式三:使用 refthis.$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
    • 第三方动画库
  • 代理服务器

    • 使用 axios 向后端请求数据,由于同源策略的限制(浏览器的同源策略)会产生跨域问题。使用代理服务器的方式解决(代理服务器与后端使用 http 进行交互不受同源策略的限制)
    • 配置 vue.config.js
    • 注意:如果访问的资源本身就有,就不会走代理服务器找
    • 高级:根据路径前缀配置多个代理方式,使用 pathRewrite: {'^/api':''} 重写路径
  • vue-resource 请求库,需要使用 Vue.use 进行安装,使用类似 axios this.$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中,与【全局事件总线】不同的是将数据放在一个独立的位置
image-20220330170026255
  • 搭建 Vuex 环境

  • actions:处理业务逻辑;mutations:直接操作数据。如果没有业务逻辑可以直接使用 $store.commit() 处理数据

  • 开发工具的使用(开发者工具与 mutations 交互)

  • actions 中的复用

  • getters 定义数据加工逻辑,通过 getters 暴露出去,类似 computed

  • mapState, mapActions, mapMutation, mapGetters 简化 computedmethods 中的代码量

  • 多组件之间共享数据

  • 使用命名空间

  • 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 简化写法怎么用:

image-20220330191648615

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库

posted @ 2023-02-28 22:29  egu0o  阅读(17)  评论(0编辑  收藏  举报