首页 > 其他分享 >​Vue3插件机制剖析 + 手写实现Vuex

​Vue3插件机制剖析 + 手写实现Vuex

时间:2022-10-19 14:04:08浏览次数:46  
标签:插件 const app counter state Vue3 Vuex options store

使用插件

这里以vuex使用为例,我们通过​​app.use(store)​​引入vuex插件:

<div id="app">
<h3 @click="add">{{counter}}</h3>
</div>

<script src="http://unpkg.com/vue@next"></script>
<script src="http://unpkg.com/vuex@next"></script>
<script>
const { createApp, reactive, computed } = Vue
const { createStore, useStore } = Vuex

const app = createApp({
setup() {
const store = useStore()
const counter = computed(() store.state.counter)
const add = () {
store.commit('add')
}
return {
counter, add
}
},
})

const store = createStore({
state: {
counter: 1
},
mutations: {
add(state) {
state.counter++
}
}
})
app.use(store)
app.mount('#app')
</script>

源码剖析

看了​​vuex​​​引入,我有个疑问,​​app.use()​​这个方法都做了什么事情?

我想到这个​​store​​​要在所有组件中都能访问到,即​​this.$store​​​,那么在​​app.use()​​​内部肯定有个操作就是设置全局属性:​​app.globalProperties.$store = store​​​,文档中说vue插件一定要实现一个​​install​​​方法,这个目的就很明显了,​​app.use()​​​会调用​​store​​​提供的这个​​install​​​方法,并把​​app​​传给它。

下面去源码中验证一下:

添加插件:app.use()

packages/runtime-core/src/apiCreateApp.ts

const app: App = (context.app = {
use(plugin: Plugin, ...options: any[]) {
if (installedPlugins.has(plugin)) {
__DEV__ && warn(`Plugin has already been applied to target app.`)
} else if (plugin && isFunction(plugin.install)) {
// plugin包含install函数
installedPlugins.add(plugin)
// 传入App实例,从而对其进行扩展
plugin.install(app, ...options)
} else if (isFunction(plugin)) {
// plugin就是一个函数
installedPlugins.add(plugin)
plugin(app, ...options)
} else if (__DEV__) {
warn(
`A plugin must either be a function or an object with an "install" ` +
`function.`
)
}
return app
},
})

看use的代码,果然调用install并传入了app,下面再验证一下vuex内部的操作,store.js

class Store {
install (app, injectKey) {
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
}
}

可见,除了设置全局属性$store,还provide了一个storeKey,这显然是为​​useStore()​​做准备,injectKey.js:

export function useStore (key = null) {
return inject(key !== null ? key : storeKey)
}

造轮子

下面我们小试牛刀,把插件基本结构实现一下。

mini-vue基本结构

<div id="app">
<h3 @click="add">{{counter}}</h3>
</div>

<script src="http://unpkg.com/vue@next"></script>
<script src="http://unpkg.com/vuex@next"></script>
<script>
const { ref, computed, watchEffect } = Vue
const { createStore, useStore } = Vuex

const MiniVue = {
createApp(options) {
return {
mount(selector) {
const parent = document.querySelector(selector)
const ctx = options.setup()

watchEffect(() {
const el = options.render.call(ctx)
parent.innerHTML = ''
parent.appendChild(el)
})
},
}
}
}

const app = MiniVue.createApp({
setup() {
const counter = ref(1)
const add = () {
counter.value++
}
return {
counter, add
}
},
render() {
const h3 = document.createElement('h3')
h3.innerHTML = this.counter.value
h3.addEventListener('click', this.add)
return h3
},
})
app.mount('#app')
</script>

这里我刻意忽略了响应式和vdom,大家可以专注于插件功能实现,

实现use方法

这里简化了use的实现形式,用户只能传入对象类型插件。

const MiniVue = {
createApp(options) {
return {
use(plugin, ...options) {
if(plugin && typeof plugin.install === 'function') {
plugin.install(app, ...options)
} else {
console.warn('插件必须是一个自带install函数的对象');
}
}
}
}
}

实现mini-vuex

下面我们实现一个简版vuex,试试我们插件体系能否生效。我们有几个小目标:

  • 声明一个Store类:包含响应式state,可预测修改state,install方法
  • 获取Store实例:createStore()
  • composition api:useStore()

声明一个Store类

Store类包含响应式state

class Store {
constructor(options) {
this._state = reactive({
data: options.state
})
}
get state() {
return this._state.data
}
}

能够可预测的修改state(commit mutation)

class Store {
constructor(options) {
this._mutations = options.mutations
}
get state() {
return this._state.data
}
commit(type, payload) {
const entry = this._mutations[type]
entry(this.state, payload)
}
}

实现install方法,满足vue插件要求:

const map = {}class Store {  install(app, injectKey) {    // app.config.globalProperties.$store = this    // provide(injectKey || 'store', this)    map[injectKey || 'store'] = this  }}

提供相关API: createStore()/useStore()

创建Store实例,实现​​createStore()​

function createStore(options) {  return new Store(options)}

实现​​useStore​​方法,可以在setup中获取store实例:

function useStore(key = null) {  return map[key !== null ? key : 'store']}

应用mini-vuex

创建Store实例

const store = createStore({  state: {    counter: 1  },  mutations: {    add(state) {      state.counter++    }  }})

使用store

app.use(store)

使用store中的数据

const app = MiniVue.createApp({  setup() {    // const counter = ref(1)    // const add = () => {    //   counter.value++    // }    const store = useStore()    const counter = computed(() => store.state.counter)    const add = () => {      store.commit('add')    }    return {      counter, add    }  },})

完整代码

<div id="app">  <h3 @click="add">{{counter}}</h3></div><script src="http://unpkg.com/vue@next"></script><script src="http://unpkg.com/vuex@next"></script><script>  const { reactive, ref, computed, watchEffect, provide, inject } = Vue  // const { createStore, useStore } = Vuex  const MiniVue = {    createApp(options) {      return {        mount(selector) {          const parent = document.querySelector(selector)          const ctx = options.setup()          watchEffect(() => {            const el = options.render.call(ctx)            parent.innerHTML = ''            parent.appendChild(el)          })        },        use(plugin, ...options) {          if (plugin && typeof plugin.install === 'function') {            plugin.install(app, ...options)          } else {            console.warn('插件必须是一个自带install函数的对象');          }        }      }    }  }  const map = {}  // Store  class Store {    constructor(options) {      this._state = reactive({        data: options.state      })      this._mutations = options.mutations    }    get state() {      return this._state.data    }    commit(type, payload) {      const entry = this._mutations[type]      entry(this.state, payload)    }    install(app, injectKey) {      // app.config.globalProperties.$store = this      // provide(injectKey || 'store', this)      map[injectKey || 'store'] = this    }  }  function useStore(key = null) {    return map[key !== null ? key : 'store']  }  function createStore(options) {    return new Store(options)  }  const store = createStore({    state: {      counter: 1    },    mutations: {      add(state) {        state.counter++      }    }  })  const app = MiniVue.createApp({    setup() {      // const counter = ref(1)      // const add = () => {      //   counter.value++      // }      const store = useStore()      const counter = computed(() => store.state.counter)      const add = () => {        store.commit('add')      }      return {        counter, add      }    },    render() {      const h3 = document.createElement('h3')      h3.innerHTML = this.counter.value      h3.addEventListener('click', this.add)      return h3    },  })  app.use(store)  app.mount('#app')</script>


标签:插件,const,app,counter,state,Vue3,Vuex,options,store
From: https://blog.51cto.com/u_15680963/5769769

相关文章

  • 如何在Azkaban中安装HDFS插件以及与CDH集成
    温馨提示:要看高清无码套图,请使用手机打开并单击图片放大查看。Fayson的github:https://github.com/fayson/cdhproject提示:代码块部分可以左右滑动查看噢1.文档编写目的前面Fa......
  • VUE3.0 中如何使用SVG图标
    1.文件下新建SvgIcon文件夹以及子文件index.js,index.vue,svg文件夹(用于存放svg图片) 2.编写index.vue组件<template><svg:class="svgClass"aria-hidden="true">......
  • 在FinClip中如何使用小程序插件?
    最近总发现很多萌新把小程序插件和小程序组件搞混淆。简单来说,组件和插件的区别在于,插件是可以直接提供服务的,组件是给开发者的轮子提高开发效率的,这是两回事。小程序插件是......
  • 提高编程效率的5大VS Code插件
    前言作为一名开发人员,大家会一直寻找可以帮助改进日常工作流程的解决方案,VSCode市场中就有很多优秀的扩展插件程序。正文​​一、GitLive​​GitLive是一个出色的扩展程......
  • 从0搭建vue3组件库:Shake抖动组件
    先看下效果其实就是个抖动效果组件,实现起来也非常简单。之所以做这样一个组件是为了后面写Form表单的时候会用到它做一个规则校验,比如下面一个简单的登录页面,当点击登录......
  • 用Typescript 的方式封装Vue3的表单绑定,支持防抖等功能。
    Vue3的父子组件传值、绑定表单数据、UI库的二次封装、防抖等,想来大家都很熟悉了,本篇介绍一种使用Typescript的方式进行统一的封装的方法。基础使用方法Vue3对于表单的绑......
  • 微信开发工具自定义扩展插件
    1、Vscode安装Easy-less2、扩展文件夹用everything搜索mrcrowl-easy-less3、复制出来放桌面,然后通过以下步骤用微信开发工具导入扩展插件就OK了4、输出wxss文......
  • vue3+vite+ts自动引入api和组件
    安装cnpminstallunplugin-auto-importunplugin-vue-components-d配置//自动导入compositionapi和生成全局typescript说明importAutoImportfrom'unplugin-au......
  • Vue 插件:VueRouter
    VueRouter是一个Vue插件,用于实现SPA(singlepagewebapplication)应用。SPA(singlepagewebapplication)应用,即单页面应用。整个应用只有一个.html文件,通常命名为......
  • mybatis-plugin插件执行原理
    mybatis-plugin插件执行原理今天主要是在看mybatis的主流程源码,其中比较感兴趣的是mybatis的plugin功能,这里主要记录下mybatis-plugin的插件功能原理。plugin集合列表:在......