App.vue
<template>
<div id="app">
div>
template>
<script>
const mixin2={
created(){
console.log("mixin created");
}
}
export default {
name: 'App',
mixins:[mixin2],
data(){
return {
message:"inner Hello world"
}
},
created(){
console.log("inner created");
}
}
script>
<style>
style>
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
const mixin1={
created:function(){
console.log("global created");
console.log("message",this.$data.message);
}
}
Vue.mixin(mixin1)
new Vue({
render: h => h(App),
}).$mount('#app')

App.vue
<template>
<div id="app">
div>
template>
<script>
const mixin2={
created(){
// console.log("mixin created");
},
data(){
return {
message:"mixin message"
}
}
}
export default {
name: 'App',
mixins:[mixin2],
data(){
return {
message:"inner message"
}
},
created(){
// console.log("inner created");
}
}
script>
<style>
style>
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
const mixin1={
created:function(){
// console.log("global created");
console.log("message",this.$data.message);
},
data(){
return{
message:"global message"
}
}
}
Vue.mixin(mixin1)
new Vue({
render: h => h(App),
}).$mount('#app')

global注入到new Vue实例上,然后在App.vue的实例中执行自己当前的data
与Vue实例执行顺序有关,先进行当前实例化声明(全局注入),然后再实例化声明的时候注入我们内部的mixin,是外部的的global更高一点
像Vue-router、Vuex等也是插件,通过插件注入进去的
Vue-router
Vue-router关于插件的内容
Vuex


install的方法,将全部内容挂载到Vue的构造函数上,通过use方法挂载到Vue实例上
Vue2中的VueRouter解析:
// for Vue2
// 这两行导入了Vue Router 提供的两个组件
import View from './components/view' //导入RouterView 渲染当前路由对应的组件
import Link from './components/link' //导入RouterLink 链接组件,导航到不同的路由
export let _Vue //定义全局变量,存储当前的Vue构造函数
export function install (Vue) { //Vue插件的入口函数,接受Vue构造函数作为参数
if (install.installed && _Vue === Vue) return //检查这个插件是否已经安装,如果安装则返回
install.installed = true //没有安装则设置为true
_Vue = Vue //并且将Vue构造函数保存到_Vue变量中
const isDef = v => v !== undefined //isDef用于检查变量是否定义
const registerInstance = (vm, callVal) => { //用于注册路由实例, vm:当前的 Vue 组件实例。callVal:传递给 registerRouteInstance 函数的值
//函数首先检查 _parentVnode 是否存在,然后检查 _parentVnode.data 和 _parentVnode.data.registerRouteInstance 是否存在。如果所有这些属性都存在,则调用 registerRouteInstance 函数,并将 vm 和 callVal 作为参数传递。
let i = vm.$options._parentVnode
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal)
}
}
// 全局注入
Vue.mixin({
beforeCreate () {
if (isDef(this.$options.router)) { //如果存在,则意味着这个组件或其父组件中定义了路由
this._routerRoot = this //当前组件实例设置为路由根实例
this._router = this.$options.router //便于后续访问路由实例
this._router.init(this) //初始化路由
// 定义响应式路由对象,为当前组件实例定义响应式_route属性,初始化为当前路由对象,当路由变化时,组件会响应式的更新
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
// 如果当前组件没有定义路由,则查找父组件的路由根实例,并且赋值给this._routerRoot
// 若当前父组件也没有定义路由,则当前组件本身 就是路由根实例
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
registerInstance(this, this)
},
destroyed () {
registerInstance(this)
}
})
// Vue原型添加 $router 属性,可以在Vue组件中通过 this.$router访问到路由实例
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
// Vue原型添加 $route 属性,可以在Vue组件中通过 this.$route访问到当前路由对象
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
// 注册全局组件,将RouterView和RouterLink注册为全局组件,使得在任何Vue组件中使用他们
Vue.component('RouterView', View)
Vue.component('RouterLink', Link)
// 定义路由钩子合并策略
// 获取Vue.js配置中的选项合并策略,并且赋值给strats变量【在 Vue.js 中,组件选项(如 data、methods、computed 等)可能会在父组件和子组件中同时定义,选项合并策略就是用来决定当父组件和子组件的相同选项冲突时如何合并的】
const strats = Vue.config.optionMergeStrategies
// 将 strats.created 的合并策略赋值给 beforeRouteEnter、beforeRouteLeave 和 beforeRouteUpdate 这三个路由守卫
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
// beforeRouteEnter、beforeRouteLeave 和 beforeRouteUpdate 并不是 Vue 组件的默认选项,而是 Vue Router 提供的路由守卫
// 这段代码中,它们被赋予了与 created 生命周期钩子相同的合并策略
// 这意味着,如果这些路由守卫在父组件和子组件中都被定义,那么它们会按照 created 生命周期钩子的合并策略进行合并
}
Vue3中的VueRouter解析:
// for Vue3
install(app: App) {
const router = this
// 将RouterLink和RouterView注册到Vue应用中,这样就可以在应用的组件中使用他们
app.component('RouterLink', RouterLink)
app.component('RouterView', RouterView)
// 将路由实例设置为Vue应用的全局属性$router,这样就可以在任何组件中通过this.$router访问路由实例了
app.config.globalProperties.$router = router
// 定义响应式的$route,用于获取当前的路由对象
Object.defineProperty(app.config.globalProperties, '$route', {
enumerable: true,
get: () => unref(currentRoute),
})
// 用于浏览器环境下,如果路由未启动,并且当前的路由是初始路由的时候,则启动路由
if (
isBrowser &&
!started &&
currentRoute.value === START_LOCATION_NORMALIZED
) {
// 启动路由
started = true
push(routerHistory.location).catch(err => {
if (__DEV__) warn('Unexpected error when starting the router:', err)
})
}
// 创建响应式路由对象,该对象包含了当前路由的所有属性,并且这些属性都是响应式的
const reactiveRoute = {} as {
[k in keyof RouteLocationNormalizedLoaded]: ComputedRef<
RouteLocationNormalizedLoaded[k]
>
}
for (const key in START_LOCATION_NORMALIZED) {
// @ts-expect-error: the key matches
reactiveRoute[key] = computed(() => currentRoute.value[key])
}
// 提供了路由相关的数据,这样其他组件可以通过inject方法获取这些数据
app.provide(routerKey, router)
app.provide(routeLocationKey, reactive(reactiveRoute))
app.provide(routerViewLocationKey, currentRoute)
// 修改了 Vue 应用的卸载方法,卸载应用之前,执行了一些与路由相关的清理工作
const unmountApp = app.unmount
installedApps.add(app)
app.unmount = function () {
installedApps.delete(app)
// the router is not attached to an app anymore
if (installedApps.size < 1) {
// invalidate the current navigation
pendingLocation = START_LOCATION_NORMALIZED
removeHistoryListener && removeHistoryListener()
removeHistoryListener = null
currentRoute.value = START_LOCATION_NORMALIZED
started = false
ready = false
}
unmountApp()
}
// 添加开发工具集成
// 如果当前是开发环境,或开启了开发工具集成,并且代码运行在浏览器环境下
if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && isBrowser) {
addDevtools(app, router, matcher) //则添加与Vue Devtools的集成
}
}
全局中定义

局部中定义:

App.vue:
<template>
<div>
<div id="hook-arguments-example" v-demo:foo.a.b="message">div>
div>
template>
<script>
export default {
data() {
return {
message: 'hello!'
}
},
directives:{
demo:{
bind: function (el, binding, vnode) {
var s = JSON.stringify
el.innerHTML =
'name: ' + s(binding.name) + '
' +
'value: ' + s(binding.value) + '
' +
'expression: ' + s(binding.expression) + '
' +
'argument: ' + s(binding.arg) + '
' +
'modifiers: ' + s(binding.modifiers) + '
' +
'vnode keys: ' + Object.keys(vnode).join(', ')
console.log("vnode",vnode);
}
}
}
}
script>
<style scoped>
style>

权限验证:v-permission
插件化的方式注入的

指令实现的功能:

const checkPermission=(el,binding)=>{
const {value}=binding
if(value instanceof Array){
if(!value.length){
//没有内容就用父节点删除子节点
el.parentNode.removeChild(el)
}
const roles=store.rule; //当前用户的权限
//查找是否有权限
const hasPermission=roles.some(role=>value.includes(role))
//没有权限就删除
if(!hasPermission){
el.parentNode.removeChild(el)
}
}else{
throw new Error("type error")
}
}
Vue.directives('permission',{
inserted(el,binding){
checkPermission(el,binding)
},
update(el,binding){
checkPermission(el,binding)
}
})
Google搜索东西举例:github awesome vue directive 在源代码中寻找
v-slot:基于web components衍生出来的
就当做占位符来看待
作用域插槽已经废弃了
<template>
<div>
{{ time|timeFormat }}
div>
template>
<script>
import dayjs from 'dayjs'
export default {
data() {
return {
time:1713401633460
}
},
filters:{
timeFormat(value,str="YYYY-MM-DD HH:mm:ss"){
return dayjs(value).format(str)
}
}
}
script>
<style scoped>
style>

要求:1.Vue官网自己过一遍