Vue3设计思想及响应式源码剖析 | 京东物流技术团队

news2024/11/16 13:35:02

一、Vue3结构分析

1、Vue2与Vue3的对比

  • 对TypeScript支持不友好(所有属性都放在了this对象上,难以推倒组件的数据类型)
  • 大量的API挂载在Vue对象的原型上,难以实现TreeShaking。
  • 架构层面对跨平台dom渲染开发支持不友好,vue3允许自定义渲染器,扩展能力强。
  • CompositionAPI。受ReactHook启发
  • 对虚拟DOM进行了重写、对模板的编译进行了优化操作…

2、Vue3设计思想

  • Vue3.0更注重模块上的拆分,在2.0中无法单独使用部分模块。需要引入完整的Vuejs(例如只想使用使用响应式部分,但是需要引入完整的Vuejs), Vue3中的模块之间耦合度低,模块可以独立使用。拆分模块
  • Vue2中很多方法挂载到了实例中导致没有使用也会被打包(还有很多组件也是一样)。通过构建工具Tree-shaking机制实现按需引入,减少用户打包后体积。重写API
  • Vue3允许自定义渲染器,扩展能力强。不会发生以前的事情,改写Vue源码改造渲染方式。扩展更方便

依然保留了Vue2的特点:

依旧是声明式框架,底层渲染逻辑不关心(命令式比较关注过程,可以控制怎么写最优?编写过程不同),如for和reduce

采用虚拟DOM

区分编译时和运行时

内部区分了编译时(模板?编程成js代码,一般在构建工具中使用)和运行时

简单来说,Vue3 框架更小,扩展更加方便

3、monorepo管理项目

Monorepo 是管理项目代码的一个方式,指在一个项目仓库(repo)中管理多个模块/包(package)。也就是说是一种将多个package放在一个repo中的代码管理模式。Vue3内部实现了一个模块的拆分, Vue3源码采用 Monorepo 方式进行管理,将模块拆分到package目录中。

  • 一个仓库可维护多个模块,不用到处找仓库
  • 方便版本管理和依赖管理,模块之间的引用,调用都非常方便
  • 每个包可以独立发布

早期使用yarn workspace + lerna来管理项目,后面是pnpm

pnpm介绍

快速,节省磁盘空间的包管理器,主要采用符号链接的方式管理模块

  1. 快速

  2. 高效利用磁盘空间

pnpm 内部使用基于内容寻址的文件系统来存储磁盘上所有的文件,这个文件系统出色的地方在于:

  • 不会重复安装同一个包。用 npm/yarn 的时候,如果 100 个项目都依赖 lodash,那么 lodash 很可能就被安装了 100 次,磁盘中就有 100 个地方写入了这部分代码。但在使用 pnpm 只会安装一次,磁盘中只有一个地方写入,后面再次使用都会直接使用hardlink(硬链接)
  • 即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。比如 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的hardlink,仅仅写入那一个新增的文件
  1. 支持Monorepo

pnpm 与 npm/yarn 一个很大的不同就是支持了 monorepo

  1. 安全性高

之前在使用 npm/yarn 的时候,由于 node_module 的扁平结构,如果 A 依赖 B, B 依赖 C,那么 A 当中是可以直接使用 C 的,但问题是 A 当中并没有声明 C 这个依赖。因此会出现这种非法访问的情况。但 pnpm自创了一套依赖管理方式,很好地解决了这个问题,保证了安全性

默认情况下,pnpm 则是通过使用符号链接的方式仅将项目的直接依赖项添加到node_modules的根目录下。

安装和初始化
  • 全局安装(node版本>16)
npm install pnpm -g



  • 初始化
pnpm init



配置workspace

根目录创建pnpm-workspace.yaml

packages:
  - 'packages/*'



将packages下所有的目录都作为包进行管理。这样我们的Monorepo就搭建好了。确实比 lerna + yarn workspace 更快捷

4、项目结构

packages

  • reactivity:响应式系统
  • runtime-core:与平台无关的运行时核心 (可以创建针对特定平台的运行时 - 自定义渲染器)
  • runtime-dom: 针对浏览器的运行时。包括DOM API,属性,事件处理等
  • runtime-test:用于测试
  • server-renderer:用于服务器端渲染
  • compiler-core:与平台无关的编译器核心
  • compiler-dom: 针对浏览器的编译模块
  • compiler-ssr: 针对服务端渲染的编译模块
  • template-explorer:用于调试编译器输出的开发工具
  • shared:多个包之间共享的内容
  • vue:完整版本,包括运行时和编译器
                                    +---------------------+
                                    |                     |
                                    |  @vue/compiler-sfc  |
                                    |                     |
                                    +-----+--------+------+
                                          |        |
                                          v        v
                      +---------------------+    +----------------------+
                      |                     |    |                      |
        +------------>|  @vue/compiler-dom  +--->|  @vue/compiler-core  |
        |             |                     |    |                      |
   +----+----+        +---------------------+    +----------------------+
   |         |
   |   vue   |
   |         |
   +----+----+        +---------------------+    +----------------------+    +-------------------+
        |             |                     |    |                      |    |                   |
        +------------>|  @vue/runtime-dom   +--->|  @vue/runtime-core   +--->|  @vue/reactivity  |
                      |                     |    |                      |    |                   |
                      +---------------------+    +----------------------+    +-------------------+



scripts

Vue3在开发环境使用esbuild打包,生产环境采用rollup打包

包的相互依赖

安装

把packages/shared安装到packages/reactivity

pnpm install @vue/shared@workspace --filter @vue/reactivity


使用

在reactivity/src/computed.ts中引入shared中相关方法

import { isFunction, NOOP } from '@vue/shared' // ts引入会报错

const onlyGetter = isFunction(getterOrOptions)
  if (onlyGetter) {
    ...
  } else {
    ...
  }
...



tips:@vue/shared引入会报错,需要在tsconfig.json中配置

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@vue/compat": ["packages/vue-compat/src"],
      "@vue/*": ["packages/*/src"],
      "vue": ["packages/vue/src"]
    }
  },
}



5、打包

所有包的入口均为src/index.ts这样可以实现统一打包.

• reactivity/package.json

{
  "name": "@vue/reactivity",
  "version": "3.2.45",
  "main": "index.js",
  "module":"dist/reactivity.esm-bundler.js",
  "unpkg": "dist/reactivity.global.js",
  "buildOptions": {
    "name": "VueReactivity",
    "formats": [
      "esm-bundler",
      "cjs",
      "global"
    ]
  }
}



• shared/package.json

{
    "name": "@vue/shared",
    "version": "3.2.45",
    "main": "index.js",
    "module": "dist/shared.esm-bundler.js",
    "buildOptions": {
        "formats": [
            "esm-bundler",
            "cjs"
        ]
    }
}



formats 为自定义的打包格式,有 esm-bundler 在构建工具中使用的格式、 esm-browser 在浏览器中使用的格式、 cjs 在node中使用的格式、 global 立即执行函数的格式

开发环境esbuild打包

开发时 执行脚本, 参数为要打包的模块

"scripts": {
    "dev": "node scripts/dev.js reactivity -f global"
}



// Using esbuild for faster dev builds.
// We are still using Rollup for production builds because it generates
// smaller files w/ better tree-shaking.

// @ts-check
const { build } = require('esbuild')
const nodePolyfills = require('@esbuild-plugins/node-modules-polyfill')
const { resolve, relative } = require('path')
const args = require('minimist')(process.argv.slice(2))

const target = args._[0] || 'vue'
const format = args.f || 'global'
const inlineDeps = args.i || args.inline
const pkg = require(resolve(__dirname, `../packages/${target}/package.json`))

// resolve output
const outputFormat = format.startsWith('global')
  ? 'iife'
  : format === 'cjs'
  ? 'cjs'
  : 'esm'

const postfix = format.endsWith('-runtime')
  ? `runtime.${format.replace(/-runtime$/, '')}`
  : format

const outfile = resolve(
  __dirname,
  `../packages/${target}/dist/${
    target === 'vue-compat' ? `vue` : target
  }.${postfix}.js`
)
const relativeOutfile = relative(process.cwd(), outfile)

// resolve externals
// TODO this logic is largely duplicated from rollup.config.js
let external = []
if (!inlineDeps) {
  // cjs & esm-bundler: external all deps
  if (format === 'cjs' || format.includes('esm-bundler')) {
    external = [
      ...external,
      ...Object.keys(pkg.dependencies || {}),
      ...Object.keys(pkg.peerDependencies || {}),
      // for @vue/compiler-sfc / server-renderer
      'path',
      'url',
      'stream'
    ]
  }

  if (target === 'compiler-sfc') {
    const consolidateDeps = require.resolve('@vue/consolidate/package.json', {
      paths: [resolve(__dirname, `../packages/${target}/`)]
    })
    external = [
      ...external,
      ...Object.keys(require(consolidateDeps).devDependencies),
      'fs',
      'vm',
      'crypto',
      'react-dom/server',
      'teacup/lib/express',
      'arc-templates/dist/es5',
      'then-pug',
      'then-jade'
    ]
  }
}

build({
  entryPoints: [resolve(__dirname, `../packages/${target}/src/index.ts`)],
  outfile,
  bundle: true,
  external,
  sourcemap: true,
  format: outputFormat,
  globalName: pkg.buildOptions?.name,
  platform: format === 'cjs' ? 'node' : 'browser',
  plugins:
    format === 'cjs' || pkg.buildOptions?.enableNonBrowserBranches
      ? [nodePolyfills.default()]
      : undefined,
  define: {
    __COMMIT__: `"dev"`,
    __VERSION__: `"${pkg.version}"`,
    __DEV__: `true`,
    __TEST__: `false`,
    __BROWSER__: String(
      format !== 'cjs' && !pkg.buildOptions?.enableNonBrowserBranches
    ),
    __GLOBAL__: String(format === 'global'),
    __ESM_BUNDLER__: String(format.includes('esm-bundler')),
    __ESM_BROWSER__: String(format.includes('esm-browser')),
    __NODE_JS__: String(format === 'cjs'),
    __SSR__: String(format === 'cjs' || format.includes('esm-bundler')),
    __COMPAT__: String(target === 'vue-compat'),
    __FEATURE_SUSPENSE__: `true`,
    __FEATURE_OPTIONS_API__: `true`,
    __FEATURE_PROD_DEVTOOLS__: `false`
  },
  watch: {
    onRebuild(error) {
      if (!error) console.log(`rebuilt: ${relativeOutfile}`)
    }
  }
}).then(() => {
  console.log(`watching: ${relativeOutfile}`)
})




生产环境rollup打包

具体代码参考rollup.config.mjs

build.js

二、Vue3中Reactivity模块

1、vue3对比vue2的响应式变化

  • 在Vue2的时候使用defineProperty来进行数据的劫持, 需要对属性进行重写添加gettersetter性能差
  • 当新增属性和删除属性时无法监控变化。需要通过$set$delete实现
  • 数组不采用defineProperty来进行劫持 (浪费性能,对所有索引进行劫持会造成性能浪费)需要对数组单独进行处理

Vue3中使用Proxy来实现响应式数据变化。从而解决了上述问题

2、CompositionAPI

  • 在Vue2中采用的是OptionsAPI, 用户提供的data,props,methods,computed,watch等属性 (用户编写复杂业务逻辑会出现反复横跳问题)
  • Vue2中所有的属性都是通过this访问,this存在指向明确问题
  • Vue2中很多未使用方法或属性依旧会被打包,并且所有全局API都在Vue对象上公开。Composition API对 tree-shaking 更加友好,代码也更容易压缩。
  • 组件逻辑共享问题, Vue2 采用mixins 实现组件之间的逻辑共享; 但是会有数据来源不明确,命名冲突等问题。 Vue3采用CompositionAPI 提取公共逻辑非常方便

简单的组件仍然可以采用OptionsAPI进行编写,compositionAPI在复杂的逻辑中有着明显的优势~。 reactivity 模块中就包含了很多我们经常使用到的 API 例如:computed、reactive、ref、effect等

3、基本使用

const { effect, reactive } = VueReactivity
// console.log(effect, reactive);
const state = reactive({name: 'qpp', age:18, address: {city: '南京'}})
console.log(state.address);
effect(()=>{
    console.log(state.name)
})



4、reactive实现

import { mutableHandlers } from'./baseHandlers'; 
// 代理相关逻辑import{ isObject }from'./util';// 工具方法
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}
function createReactiveObject(target, baseHandler){
    if(!isObject(target)){
        return target;
    }
    ...
    const observed =new Proxy(target, baseHandler);
    return observed
}



baseHandlers

import { isObject, hasOwn, hasChanged } from"@vue/shared";
import { reactive } from"./reactive";
const get = createGetter();
const set = createSetter();
function createGetter(){
    return function get(target, key, receiver){
        // 对获取的值进行放射
        const res = Reflect.get(target, key, receiver);
        console.log('属性获取',key)
        if(isObject(res)){// 如果获取的值是对象类型,则返回当前对象的代理对象
            return reactive(res);
        }
        return res;
    }
}
function createSetter(){
    return function set(target, key, value, receiver){
        const oldValue = target[key];
        const hadKey =hasOwn(target, key);
        const result = Reflect.set(target, key, value, receiver);
        if(!hadKey){
            console.log('属性新增',key,value)
        }else if(hasChanged(value, oldValue)){
            console.log('属性值被修改',key,value)
        }
        return result;
    }
}
export const mutableHandlers ={
    get,// 当获取属性时调用此方法
    set// 当修改属性时调用此方法
}



这里我只选了对最常用到的get和set方法的代码,还应该有 hasdeletePropertyownKeys 。这里为了快速掌握核心流程就先暂且跳过这些代码

5、effect实现

我们再来看effect的代码,默认effect会立即执行,当依赖的值发生变化时effect会重新执行

export let activeEffect = undefined;
// 依赖收集的原理是 借助js是单线程的特点, 默认调用effect的时候会去调用proxy的get,此时让属性记住
// 依赖的effect,同理也让effect记住对应的属性
// 靠的是数据结构 weakMap : {map:{key:new Set()}}
// 稍后数据变化的时候 找到对应的map 通过属性出发set中effect
function cleanEffect(effect) {
    // 需要清理effect中存入属性中的set中的effect 
    // 每次执行前都需要将effect只对应属性的set集合都清理掉
    // 属性中的set 依然存放effect
    let deps = effect.deps
    for (let i = 0; i < deps.length; i++) {
        deps[i].delete(effect)
    }
    effect.deps.length = 0;

}

// 创建effect时可以传递参数,computed也是基于effect来实现的,只是增加了一些参数条件而已
export function effect<T = any>(
    fn: () => T,
    options?: ReactiveEffectOptions    
){
    // 将用户传递的函数编程响应式的effect
    const _effect = new ReactiveEffect(fn,options.scheduler);
    // 更改runner中的this
    _effect.run()
    const runner = _effect.run.bind(_effect);
    runner.effect = _effect; // 暴露effect的实例
    return runner// 用户可以手动调用runner重新执行
}
export class ReactiveEffect {
    public active = true;
    public parent = null;
    public deps = []; // effect中用了哪些属性,后续清理的时候要使用
    constructor(public fn,public scheduler?) { } // 你传递的fn我会帮你放到this上
    // effectScope 可以来实现让所有的effect停止
    run() {
        // 依赖收集  让熟悉和effect 产生关联
        if (!this.active) {
            return this.fn();
        } else {
            try {
                this.parent = activeEffect
                activeEffect = this;
                cleanEffect(this); // vue2 和 vue3中都是要清理的 
                return this.fn(); // 去proxy对象上取值, 取之的时候 我要让这个熟悉 和当前的effect函数关联起来,稍后数据变化了 ,可以重新执行effect函数
            } finally {
                // 取消当前正在运行的effect
                activeEffect = this.parent;
                this.parent = null;
            }
        }
    }
    stop() {
        if (this.active) {
            this.active = false;
            cleanEffect(this);
        }
    }
}



在effect方法调用时会对属性进行取值,此时可以进行依赖收集。

effect(()=>{
    console.log(state.name)
    // 执行用户传入的fn函数,会取到state.name,state.age... 会触发reactive中的getter
    app.innerHTML = 'name:' + state.name + 'age:' + state.age + 'address' + state.address.city
    
})



6、依赖收集

核心代码
// 收集属性对应的effect
export function track(target, type, key){}// 触发属性对应effect执行
export function trigger(target, type, key){}


function createGetter(){
    return function get(target, key, receiver){
        const res = Reflect.get(target, key, receiver);
        // 取值时依赖收集
        track(target, TrackOpTypes.GET, key);
        if(isObject(res)){
            return reactive(res);
        }
        return res;
    }
}



function createSetter(){
    return function set(target, key, value, receiver){
        const oldValue = target[key];
        const hadKey =hasOwn(target, key);
        const result = Reflect.set(target, key, value, receiver);
        if(!hadKey){
            // 设置值时触发更新 - ADD
            trigger(target, TriggerOpTypes.ADD, key);
        }else if(hasChanged(value, oldValue)){
             // 设置值时触发更新 - SET
            trigger(target, TriggerOpTypes.SET, key, value, oldValue);
        }
        return result;
    }
}



track的实现

const targetMap = new WeakMap();
export function track(target: object, type: TrackOpTypes, key: unknown){
    if (shouldTrack && activeEffect) { // 上下文 shouldTrack = true
        let depsMap = targetMap.get(target);
        if(!depsMap){// 如果没有map,增加map
            targetMap.set(target,(depsMap =newMap()));
        }
        let dep = depsMap.get(key);// 取对应属性的依赖表
        if(!dep){// 如果没有则构建set
            depsMap.set(key,(dep =newSet()));
        }
    
        trackEffects(dep, eventInfo)
    }
}

export function trackEffects(
  dep: Dep,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  //let shouldTrack = false
  //if (effectTrackDepth <= maxMarkerBits) {
   // if (!newTracked(dep)) {
     // dep.n |= trackOpBit // set newly tracked
     // shouldTrack = !wasTracked(dep)
    //}
  //} else {
    // Full cleanup mode.
  //  shouldTrack = !dep.has(activeEffect!)
  } 

  if (!dep.has(activeEffect!) {
    dep.add(activeEffect!)
    activeEffect!.deps.push(dep)
    //if (__DEV__ && activeEffect!.onTrack) {
    //  activeEffect!.onTrack({
    //    effect: activeEffect!,
    //    ...debuggerEventExtraInfo!
    //  })
   // }
  }
}



trigger实现

export function trigger(target, type, key){
    const depsMap = targetMap.get(target);
    if(!depsMap){
        return;
    }
    const run=(effects)=>{
        if(effects){ effects.forEach(effect=>effect()); }
    }
    // 有key 就找到对应的key的依赖执行
    if(key !==void0){
        run(depsMap.get(key));
    }
    // 数组新增属性
    if(type == TriggerOpTypes.ADD){
        run(depsMap.get(isArray(target)?'length':'');
    }}



依赖关系

作者:京东物流 乔盼盼

来源:京东云开发者社区 自猿其说Tech 转载请注明来源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1250751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阅读笔记——《Removing RLHF Protections in GPT-4 via Fine-Tuning》

【参考文献】Zhan Q, Fang R, Bindu R, et al. Removing RLHF Protections in GPT-4 via Fine-Tuning[J]. arXiv preprint arXiv:2311.05553, 2023.【注】本文仅为作者个人学习笔记&#xff0c;如有冒犯&#xff0c;请联系作者删除。 目录 摘要 一、介绍 二、背景 三、方法…

十大排序之冒泡排序与快速排序(详解)

文章目录 &#x1f412;个人主页&#x1f3c5;算法思维框架&#x1f4d6;前言&#xff1a; &#x1f380;冒泡排序 时间复杂度O(n^2)&#x1f387;1. 算法步骤思想&#x1f387;2.动画实现&#x1f387; 3.代码实现&#x1f387;4.代码优化&#xff08;添加标志量&#xff09; …

高级搜索-线段树[C/C++]

线段树 文章目录 线段树前言一、线段树的定义二、线段树的结构与建立2..1 节点定义2.2 递归建树2.3 静态数组空间的解释 三、线段树的操作3.1 单点修改3.2 单点查询3.3 区间查询3.3 区间修改 四、动态开点线段树递增分配器 前言 对于求数组区间和我们可以处理出前缀和后可以在…

京东数据采集接口推荐(京东大数据分析工具)

随着京东电商平台的不断发展&#xff0c;平台中店铺数量也越来越多&#xff0c;对于电商卖家而言&#xff0c;在电商运营过程中如何做好数据分析也越来越重要。而电商运营数据往往多而杂&#xff0c;想要高效的完成电商数据分析&#xff0c;品牌需要借助一些电商数据分析软件。…

Leetcode—1457.二叉树中的伪回文路径【中等】

2023每日刷题&#xff08;四十&#xff09; Leetcode—1457.二叉树中的伪回文路径 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ int record[10] {0};int accumula…

Qt C++中调用python,并将软件打包发布,python含第三方依赖

工作中遇到qt c调用我的python 代码&#xff0c;并且想要一键打包&#xff0c;这里我根据参考的以及个人实践的结果来简单实现一下。 环境&#xff1a;windows系统&#xff0c;QT Creater 4.5&#xff0c; python 3.8&#xff08;anaconda虚拟环境&#xff09; 1. 简单QT调用…

pgz easyexcel如何给excel文件添加自定义属性

免费API方式 直接上传URL,自定义修改Excel 视频演示【内含接口地址】 https://www.ixigua.com/7304510132812153385 前情提示 | 功能说明 多选仅支持微软office、office365系列Excel。因为WPS宏功能需要企业版且付费生成xlsx、xlsm等文件,office和WPS均可以打开,均可以单…

红队攻防实战系列一之Cobalt Strike

他日若遂凌云志&#xff0c;敢笑黄巢不丈夫 本文首发于先知社区&#xff0c;原创作者即是本人 前言 在红队攻防中&#xff0c;需要我们拥有综合能力&#xff0c;不仅仅是web渗透的漏洞挖掘与利用&#xff0c;边界突破的方式有很多种&#xff0c;当然这需要我们拥有很强的意识…

(4)BUUCTF-web-[极客大挑战 2019]EasySQL1

前言&#xff1a; 觉得这个题目挺有意义的&#xff0c;因为最近在学数据库&#xff0c;但是不知道在现实中有什么应用&#xff0c;所以学起来也没有什么兴趣&#xff0c;做了这个题目&#xff0c;发现数据库还是挺有用处的&#xff0c;哈哈 知识点&#xff1a; mysql 中and和…

incast,拥塞控制,内存墙的秘密

数据中心 incast&#xff0c;广域网拥塞&#xff0c;内存墙都是一类问题。 我接触 incast 很久了&#xff0c;大多是帮忙查问题&#xff0c;也解过几例。 我记得有一次在业务几乎总是(在统计学上&#xff0c;几乎和总是属同义强调) tail latency 很大时&#xff0c;我建议在 …

【Python3】【力扣题】349. 两个数组的交集

【力扣题】题目描述&#xff1a; 【Python3】代码&#xff1a; 1、解题思路&#xff1a;集合的交集。两个数组都转为集合&#xff0c;获取集合的交集。 知识点&#xff1a;set(...)&#xff1a;转为集合&#xff0c;集合的元素不重复。 集合1.intersection(集合2)&#xff1a…

将 Hexo 部署到阿里云轻量服务器(保姆级教程)

将 Hexo 部署到阿里云轻量服务器(保姆级教程) 顺哥轻创 1 前言 作为有梦想的,有追求的程序员,有一个自己的个人博客简直就是必须品。你可以选择 wordpress 这种平台,直接使用,在任何地方只要有网络就能写博客。还可以选择 hexo 这种静态博客,但是发文章就没有那么随心…

基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境爬虫模型训练实际应用 模块实现1. 数据准备1&#xff09;爬虫下载原始图片2&#xff09;手动筛选图片 2. 数据处理1&#xff09;切割得到人物脸部2&#xff09;重新命名处理后的图片3&#xff09;添加到数据集 3. 模型训练及…

webpack 打包优化

在vue.config.js中配置 下载 uglifyjs-webpack-plugin 包 const { defineConfig } require("vue/cli-service"); var path require("path");module.exports defineConfig({transpileDependencies: true,filenameHashing: false, // 去除Vue打包后.cs…

SSM手机资讯网站系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 手机资讯网站系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模…

PHP 针对mysql 自动生成数据字典

PHP 针对mysql 自动生成数据字典 确保php 可以正常使用mysqli 扩展 这里还需要注意 数据库密码 如果密码中有特殊字符 如&#xff1a; 首先&#xff0c;我们需要了解MySQL中的特殊字符包括哪些。MySQL中的特殊字符主要包括以下几类&#xff1a; 1. 单引号&#xff08;&a…

【开源】基于Vue和SpringBoot的农家乐订餐系统

项目编号&#xff1a; S 043 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S043&#xff0c;文末获取源码。} 项目编号&#xff1a;S043&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核…

jvm优化之:OOM(out of memory)内存溢出

内存溢出 注意内存溢出不是内存泄漏&#xff01;&#xff01;这里主要是介绍如何用jdk自带的jmap工具导出进程堆空间快照。内存溢出&#xff1a; Out Of Memory&#xff0c;是指申请的堆内存空间不够用了&#xff0c;比如&#xff1a;你申请了10M空间&#xff0c;但是你要放12M…

鸿蒙开发-ArkTS 语言-循环渲染

鸿蒙开发-ArkTS 语言-状态管理 4. 渲染控制 对于 UI 渲染&#xff0c;可以基于数据结构选择一些内置方法&#xff08;例如&#xff1a;ForEach&#xff09;快速渲染 UI 结构。 4.1 if-else条件渲染 ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态&#xff0c;使…

ORA-14452: 试图创建, 变更或删除正在使用的临时表中的索引

在编写一个test存储过程中出现一个错误报告:ORA-14452: 试图创建, 变更或删除正在使用的临时表中的索引,代码如下 create or replace PROCEDURE TMP_TRANSCRIPT AS str_sql varchar2(500);v_flag number:0; --标识 begin--判断临时表是否存在SELECT COUNT(*) into v_flag FROM…