皮皮网

【agYLC源码】【死亡列车源码大全】【投资ai指标源码】vue.js 源码

时间:2024-11-15 06:28:44 来源:阿里云头像源码

1.Vue2源码解析?2?初始化
2.每天学点Vue源码: 关于vm.$watch()内部原理
3.Vue中创建的main.js代码解析
4.Vue2源码细读-new Vue()初始化
5.Vue2.6x源码解析(一):Vue初始化过程
6.Vue computed 的内部实现原理

vue.js 源码

Vue2源码解析?2?初始化

       活着,最有意义的事情,就是不遗余力地提升自己的认知,拓展自己的认知边界。

       在搭建源码调试环境一节中,我们已经找到了Vue的agYLC源码构造函数,接下来开始探索Vue初始化的流程。

一个小测试

       在精读源码之前,我们可以在一些重要的方法内打印一下日志,熟悉一下这些关键节点的执行顺序。(执行npmrundev后,源码变更后会自动生成新的Vue.js,我们的测试html只需要刷新即可)

在初始化之前,Vue类的构建过程?

       在此过程中,大部分都是原型方法和属性,意味着实例vm可以直接调用

       注意事项:

       1、以$为前缀的属性和方法,在调用_init原型方法的那一刻即可使用

       2、以_为前缀的原型方法和属性,谨慎使用

       3、本章旨在了解Vue为我们提供了哪些工具(用到时,深入研究,不必要在开始时花过多精力,后边遇到时会详细说明)

       4、类方法和属性在newVue()前后都可以使用,原型方法和属性只能在newVue()后使用

定义构造函数//src/core/instance/index.jsfunctionVue(options){ //形式上很简单,就是一个_init方法this._init(options)}挂载原型方法:_init//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }挂载与state相关的原型属性和原型方法//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}挂载与事件相关的原型方法//src/core/instance/events.jsconsthookRE=/^hook:/Vue.prototype.$on=function(event:string|Array<string>,fn:Function):Component{ }Vue.prototype.$once=function(event:string,fn:Function):Component{ }Vue.prototype.$off=function(event?:string|Array<string>,fn?:Function):Component{ }Vue.prototype.$emit=function(event:string):Component{ }挂载与生命周期相关的原型方法//src/core/instance/lifecycle.jsVue.prototype._update=function(vnode:VNode,hydrating?:boolean){ }Vue.prototype.$forceUpdate=function(){ }Vue.prototype.$destroy=function(){ }挂载与渲染相关的原型方法//installruntimeconveniencehelpersinstallRenderHelpers(Vue.prototype)Vue.prototype.$nextTick=function(fn:Function){ }Vue.prototype._render=function():VNode{ }挂载Vue类方法和类属性//src/core/global-api/index.js//configconstconfigDef={ }configDef.get=()=>configObject.defineProperty(Vue,'config',configDef)Vue.util={ warn,extend,mergeOptions,defineReactive}Vue.set=setVue.delete=delVue.nextTick=nextTick//2.6explicitobservableAPIVue.observable=<T>(obj:T):T=>{ observe(obj)returnobj}Vue.options=Object.create(null)ASSET_TYPES.forEach(type=>{ Vue.options[type+'s']=Object.create(null)})Vue.options._base=Vueextend(Vue.options.components,builtInComponents)initUse(Vue)//挂载类方法use,用于安装插件(特别特别重要)initMixin(Vue)//挂载类方法mixin,用于全局混入(在Vue3中被新特性取代)initExtend(Vue)//实现Vue.extend函数initAssetRegisters(Vue)//实现Vue.component,Vue.directive,Vue.filter函数挂载平台相关的属性,挂载原型方法$mount//src/platforms/web/runtime/index.js//installplatformspecificutilsVue.config.mustUseProp=mustUsePropVue.config.isReservedTag=isReservedTagVue.config.isReservedAttr=isReservedAttrVue.config.getTagNamespace=getTagNamespaceVue.config.isUnknownElement=isUnknownElement//installplatformruntimedirectives&componentsextend(Vue.options.directives,platformDirectives)extend(Vue.options.components,platformComponents)//installplatformpatchfunctionVue.prototype.__patch__=inBrowser?patch:noopconsole.log('挂载$mount方法')//publicmountmethodVue.prototype.$mount=function(el?:string|Element,hydrating?:boolean):Component{ }拓展$mount方法//src/platforms/web/entry-runtime-with-compiler.jsconstmount=Vue.prototype.$mount//保存之前定义的$mount方法Vue.prototype.$mount=function(el?:string|Element,hydrating?:boolean):Component{ //执行拓展内容returnmount.call(this,el,hydrating)//执行最初定义的$mount方法}Vue的初始化过程(很重要哦!!!)

       熟悉了初始化过程,就会对不同阶段挂载的实例属性了然于胸,了解Vue是如何处理options中的数据,将初始化流程抽象成一个模型,从此,当你看到用户编写的options选项,都可以在这个模型中演练。

       前边我们提到过,Vue的构造函数中只调用了一个_init方法

执行_init方法//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ constvm:Component=this//此刻,Vue的死亡列车源码大全实例已经创建,只是雏形,但Vue的所有原型方法可以调用//aflagtoavoidthisbeingobserved//(observe会在后面的响应式章节详细说明)vm._isVue=true//mergeoptionsif(options&&options._isComponent){ //在后面的Vue组件章节会详细说明//optimizeinternalcomponentinstantiation//sincedynamicoptionsmergingisprettyslow,andnoneofthe//internalcomponentoptionsneedsspecialtreatment.initInternalComponent(vm,options)}else{ vm.$options=mergeOptions(//合并optionsresolveConstructorOptions(vm.constructor),//主要处理包含继承关系的实例()options||{ },vm)}//exposerealselfvm._self=vminitLifecycle(vm)//初始化实例中与生命周期相关的属性initEvents(vm)//处理父组件传递的事件和回调initRender(vm)//初始化与渲染相关的实例属性callHook(vm,'beforeCreate')//调用beforeCreate钩子,即执行beforeCreate中的代码(用户编写)initInjections(vm)//resolveinjectionsbeforedata/props获取注入数据initState(vm)//初始化props、methods、data、computed、watchinitProvide(vm)//resolveprovideafterdata/props提供数据注入callHook(vm,'created')//执行钩子created中的代码(用户编写)if(vm.$options.el){ //DOM容器(通常是指定id的div)vm.$mount(vm.$options.el)//将虚拟DOM转换成真实DOM,然后插入到DOM容器内}}initLifecycle:初始化与生命周期相关的实例属性//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }0initEvents(vm):处理父组件传递的事件和回调//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }1initRender(vm):初始化与渲染相关的实例属性//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }2CallHook(vm,'beforeCreate'):执行beforeCreate钩子

       执行options中,用户编写在beforeCreate中的代码

//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }3initInjections(vm):resolveinjectionsbeforedata/props获取注入数据//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }4initState(vm):初始化props、methods、data、computed、watch(划重点啦!!!)//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }5initProps:初始化props

       此处概念比较多,propsData、props、vm._props、propsOptions,后续会结合实例来分析其区别,此处只做大概了解。

//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }6initMethods:初始化methods//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }7initData:初始化data//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }8initComputed:初始化computed选项//src/core/instance/init.jsVue.prototype._init=function(options?:Object){ }9initWatch:初始化watch

       createWatcher:本质上执行了vm.$watch(expOrFn,handler,options)

//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}0initProvide(vm):提供数据注入

       为什么provide初始化滞后与inject,后续补充

//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}1CallHook(vm,'created'):执行created钩子中的代码

       callHook的相关逻辑,参考上面的callHook(vm,'beforeCreate')

执行挂载执行$mount扩展

       通过下面的代码可知:当用户代码中同时包含render,template,el时,它们的优先级依次为:render、template、el

//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}2

       $mount方法中,首先获取挂载容器,然后执行mountComponent方法

//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}3//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}4

       在_update方法中,通过_vnode属性判断是否初次渲染,patch其实就是patch方法,关于patch的详细逻辑,将在diff算法章节详细说明。

//src/core/instance/state.jsconstdataDef={ }dataDef.get=function(){ returnthis._data}constpropsDef={ }propsDef.get=function(){ returnthis._props}Object.defineProperty(Vue.prototype,'$data',dataDef)Object.defineProperty(Vue.prototype,'$props',propsDef)Vue.prototype.$set=setVue.prototype.$delete=delVue.prototype.$watch=function(expOrFn:string|Function,cb:any,options?:Object):Function{ //略}5原文:/post/

每天学点Vue源码: 关于vm.$watch()内部原理

       深入探讨Vue源码,解析vm.$watch()的内部原理,让我们从整体结构入手。使用vm.$watch()时,首先数据属性被整个对象a进行观察,投资ai指标源码这个过程产生一个名为ob的Observe实例。在该实例中,存在dep,它代表依赖关系,而依赖关系在Observe实例内部进行存储。接下来,我们聚焦于内部实现细节,深入理解vm.$watch()在源码中的运作机制。

       在Vue的源代码中,实现vm.$watch()功能的具体位置位于`vue/src/core/instance/state.js`文件。从这里开始,我们移步至`vue/src/core/observer/watcher.js`文件,探寻更深入的实现逻辑。此文件内,watcher.js承担了关键角色,管理着观察者和依赖关系的关联。

       在深入解析源码过程中,我们发现,当使用vm.$watch()时,Vue会创建一个Watcher实例,这个实例负责监听特定属性的变化。每当被观察的属性值发生变化时,Watcher实例就会触发更新,确保视图能够相应地更新。这一过程通过依赖的管理来实现,即在Observe实例内部,依赖关系被封装并存储,确保在属性变化时能够准确地通知相关的Watcher实例。

       总的来说,vm.$watch()的内部实现依赖于Vue框架的观察者模式,通过创建Observe实例和Watcher实例来实现数据变化的监听和响应。这一机制保证了Vue应用的响应式特性,使得开发者能够轻松地在数据变化时触发视图更新,从而构建动态且灵活的应用程序。

Vue中创建的main.js代码解析

       Vue.js 应用程序的入口文件 main.js 包含关键步骤,确保应用能够运行并与用户交互。

       首行引入了 createApp 函数,这是 Vue 3 中创建应用实例的标准方法。这个函数生成一个用于创建 Vue 应用的新实例。

       接下来,导入了根组件 App.vue,源码在线怎么用这是整个应用的基础构建模块。所有其他组件和页面都是在这个组件的上下文中定义的。

       导入 Vue Router 配置,该配置在文件 ./router 中定义了应用的路由规则。这允许应用实现页面间的导航,提供用户流畅的交互体验。

       使用 createApp 函数创建了一个新的 Vue 应用实例,并传入了 App 组件。这个实例是整个应用的核心,所有功能模块都将挂载在这个实例上。

       将 Vue Router 实例添加到应用实例中,激活了路由功能,允许定义多个页面并实现页面间导航。

       最后,通过 app.mount('#app') 将应用实例挂载到 HTML 页面的指定元素上。这里 '#app' 是一个选择器,通常对应于 HTML 文件中的某个元素,比如

        标签。这一步确保了应用的内容能够显示在网页的特定部分。

       整个 main.js 文件的目的是初始化并启动 Vue.js 应用程序。它创建应用实例、配置路由,并将应用挂载到 DOM,实现了组件、路由等各部分的集成,形成一个完整、动态的交互式应用。

       挂载应用到 DOM 是指将 Vue 应用的内容插入到 HTML 页面的一个特定区域。Vue 框架接管了这个区域,并根据应用逻辑动态渲染和更新内容。这一过程使得应用成为动态、可交互的界面,提供给用户无缝的体验。

       总的来说,main.js 文件是 Vue.js 应用的核心,它负责启动应用、配置组件和路由,并将应用内容挂载到页面上,实现了应用的完整运行和用户交互。

Vue2源码细读-new Vue()初始化

       Vue.js 是一个数据驱动的前端框架,其核心是源码怎么发射炮弹通过数据生成视图,开发者更关注数据模型与流转而非视图生成。

       从 new Vue() 开始,我们将探索 Vue 实例的创建过程。新创建的 Vue 实例本质上是一个 Vue 的实例对象。Vue 作为构造函数,只能通过 new 操作符创建实例,核心功能是调用初始化方法 _init,并传入参数。

       Vue 的实现中,构造函数定义了多个 mixin,这些 mixin 被挂载到 Vue.prototype,以降低耦合度,便于维护。初始化流程包括多个模块的挂载,如初始化、数据状态、事件发布订阅、生命周期与渲染。

       初始化过程主要分为三个阶段:手动调用场景和组件场景。手动调用场景指直接创建的 Vue 实例,优先级高于组件场景。组件场景涉及全局或局部注册的组件,组件创建和继承通过 Vue.extend 实现。

       组件创建过程中,Vue.extend 用于获取组件构造函数,createComponent 则生成初始的 VNode。组件实例的创建发生在 patch 过程中,此时调用 init 钩子,真正创建组件实例。

       组件实例的 options 包含组件配置,通过对象赋值保存到实例中。在组件场景中,initInternalComponent 函数处理组件实例的初始化,包括设置组件选项和相关属性。

       综上所述,new Vue() 过程涉及构造函数的初始化、混合功能的挂载、配置的合并与组件的创建。这一过程在后续篇章中将详细分析。

       

参考资料:

[Vue.js 技术揭秘]( 合并配置 | Vue.js 技术揭秘)

Vue2.6x源码解析(一):Vue初始化过程

       Vue2.6x源码解析(一):Vue初始化过程

       Vue.js的核心代码在src/core目录,它在任何环境都能运行。项目入口通常在src/main.js,引入的Vue构造函数来自dist/vue.runtime.esm.js,这个文件导出了Vue构造函数,允许我们在创建Vue实例前预置全局API和原型方法。

       初始化前,Vue构造函数在src/core/instance/index.js中定义,它预先挂载了全局API如set、delete等。即使不通过new Vue初始化,Vue本身已具备所需功能。

       当执行new Vue时,实际上是调用了_init方法,这个过程会在src/core/index.js的initGlobalAPI(Vue)中初始化全局API和原型方法。接着,组件实例的初始化与根实例基本一致,包括组件构造函数的定义,以及组件的生命周期、渲染和挂载。

       组件初始化过程中,关键步骤包括数据转换为响应式、事件注册和watcher的创建。例如,组件的渲染函数会触发渲染方法,而watcher的更新则通过异步更新队列机制确保性能。

       在开发环境,Vue-template-compiler插件负责模板编译,然后runtime中的$mount方法负责实际的渲染和挂载。整个过程涉及组件的构建、渲染函数生成、依赖响应式数据的更新和异步调度。

Vue computed 的内部实现原理

       Vue.js的魅力在于其强大的视图层管理,其中computed属性就是一项关键技术,它简化了模板中的复杂逻辑,确保代码的清晰易读。让我们通过实例深入理解它的内部运作原理。

       简洁的模板与计算属性

       想象一下这样的场景:

       <div id="example">

        <p>Original message: { { message }}</p>

        <p>Computed reversed message: { { reversedMessage }}</p>

       </div>

       <script>

       new Vue({

        el: '#example',

        data: { message: 'Hello' },

        computed: {

        reversedMessage: function () {

        return this.message.split('').reverse().join('');

        }

        }

       });

       运行这段代码,你会看到Original message显示"Hello",Computed reversed message则是"olleH"。这是computed属性的直观应用,它确保了在模板中只显示经过计算的结果,而非原始逻辑。

       响应式与缓存机制

       Vue的计算属性并非一劳永逸地运行,而是基于依赖关系和缓存策略。当数据改变时,只有相关的计算属性才会重新计算。这就意味着,即使依赖的数据更新,只要结果没有变化,Vue会利用缓存来避免不必要的渲染,提升了性能。

       深入源码,你会发现计算属性在组件初始化时,会为每个属性创建一个Watcher对象,lazy属性默认开启,只有在首次访问时才会触发计算。这确保了在数据变化时,计算的高效执行。

       计算属性内部实现

       Vue的计算属性是通过getter方法实现的,其核心代码如下:

       <strong>getter实现: </strong>

       Object.defineProperty(target, key, {

        enumerable: true,

        configurable: true,

        get: (isServerRendering() ? noop : createComputedGetter(key)),

        set: (userDef.get ? (shouldCache && !userDef.cache ? createComputedGetter(key) : createGetterInvoker(userDef.get)) : noop)

       });

       getter内部,当依赖的数据发生变化时,计算属性的Watcher会检测到并调用evaluate方法重新计算值,同时更新其dirty标志以标记是否需要渲染。

       优化与性能

       Vue巧妙地处理了计算属性的更新策略,当值未变时,避免了不必要的组件渲染,确保了性能。这在依赖数据变化频繁但结果稳定的场景中尤为重要。

       计算属性与方法、watch的比较

       计算属性利用缓存,仅在依赖更新时重新计算,降低了对性能的影响。

       相比之下,methods是无缓存的,一旦满足条件,函数立即执行,没有依赖跟踪。

       对于异步或耗时操作,watch更适合,因为它可以更灵活地处理变化,包括设置回调和监听。

       总结来说,Vue的computed属性是高效、智能的解决方案,它将复杂的逻辑隐藏在背后,让你的模板保持简洁,同时保证了数据的实时性和性能。通过理解其内部实现,我们可以更好地利用和优化Vue应用中的计算属性。

这 8 个超赞的 Vue 开源项目你一定要知道

       Vue.js作为热门且前景广阔的前端框架,以其数据驱动和组件化思想,提供了简洁且易于理解的API,快速推动了前端项目的构建与开发。以下精选8个Vue开源项目,涵盖了各种用途,助你提升开发效率与技能:

VuePress- 一个基于Vue的静态站点生成器,以其vue、vue-router以及vue ssr等技术为核心,简化了文档编写与开发过程。Markdown语法的友好支持让开发者能专心于内容创作,而Vue组件的灵活运用则为自定义功能提供了可能。它不仅是一个强大的文档管理系统,也是一款小型CMS,适合搭建个人博客,GitHub星数为K。

Vuegg- 低代码开发的先锋,通过可视化界面和拖拽功能,Vuegg极大地简化了Vue.js项目的构建流程。设计与原型制作在单一平台上完成,生成的代码质量高且易于理解,GitHub星数为2.2K。

Vuetify- 一个专注Material设计规范的UI组件库,为开发者提供了一套时尚、易用的Material风格界面。遵循Google的Material Design语言,Vuetify不仅提供了丰富的组件,还配套了详尽的开发文档和视频教程,获得Vue.js创始人尤雨溪的认可,GitHub星数达到.6K。

Buefy- 基于Bulma的轻量级UI组件库,提供一系列即用即装的组件,虽然数量有限,但其简洁性与轻量化设计使其成为小型项目或特定需求的理想选择。GitHub星数为9.3K。

awesome-vue- Vue.js官方推荐的学习与资源项目,集合了一系列精选的Vue.js内容,涵盖了从初学者到高级开发者的各种需求,包含丰富的学习资料与示例代码,GitHub星数为.5K。

YesPlayMusic- 一款基于Vue全家桶开发的网易云音乐播放器,兼容Windows、macOS与Linux系统,界面美观,GitHub星数为.2K。

Nuxt.js- 一个专为Vue.js设计的通用应用框架,提供SSR(Server-Side Rendering)功能,通过nuxt.config.js配置文件整合了开发所需的各种工具,简化了客户端与服务端的开发流程,GitHub星数为.8K。

Statusfy- 简单的开源状态页系统,以Vue、Nuxt.js和Tailwind CSS为基础,支持静态生成与服务器渲染,让开发者轻松创建并维护状态页面,GitHub星数为2.7K。

这些Vue开源项目覆盖了从文档编写、低代码开发、UI组件设计、资源学习到应用框架与状态页系统等多个方面,是提升Vue.js开发技能与项目效率的有力工具。

用Vue轻松打造你的网站:零基础教程

       Vue.js 是一个高效、灵活且易于上手的JavaScript框架,受到许多开发者的喜爱。本文将用金字塔结构、多举例和打比方的方式,让小白也能轻松入门Vue.js。

       一、Vue.js是什么?

       Vue.js 就像一个模板,帮助你快速搭建起一个网站。它是一个JavaScript框架,用于构建用户界面。Vue.js能让你更容易地将数据和视图关联起来,处理各种网站交互,提高开发效率。

       二、Vue.js的核心概念

       数据驱动:Vue.js将数据和视图分离,你只需关注数据的变化,视图会自动更新。组件化:Vue.js支持将复杂的界面分解为可复用的组件。指令:Vue.js提供了一些特殊的HTML属性,用于操作DOM元素。

       三、Vue.js实战

       安装与引入:使用CDN引入Vue.js或使用npm安装。创建一个Vue实例:显示“Hello Vue!”。双向绑定:Vue.js 提供了双向绑定功能,将数据与视图保持同步。条件渲染:通过v-if 指令,我们可以根据条件显示或隐藏元素。列表渲染:使用v-for 指令,我们可以轻松地渲染一个列表。事件处理:Vue.js 可以通过v-on 指令监听 DOM 事件,实现交互。

       四、Vue.js生态

       Vue.js 拥有丰富的生态系统,提供了许多工具与插件,帮助你更高效地开发。

       五、总结

       Vue.js 是一个强大、灵活且易于学习的框架,让你能快速搭建出高质量的网站。通过本文的介绍,你应该对 Vue.js 有了基本的了解。接下来,你可以去学习更多关于Vue.js的知识,实操技巧。

推荐资讯
精准底部启动副图源码

精准底部启动副图源码

cobar源码

cobar源码

siri 源码

siri 源码

jxl 源码

jxl 源码

食物主义小程序 源码

食物主义小程序 源码

oexam 源码

oexam 源码

copyright © 2016 powered by 皮皮网   sitemap