发布vue3组件到npm

news2024/11/20 2:26:02

目录

准备你的组件

创建项目

创建步骤

创建组件 

封装我们要得组件

导出组件 

打包 

 使用组件

发布包

创建npm账户

登录npm

切换npm源

发布到npm

package.json

package.json字段

确定版本号

版本号格式

版本号递增逻辑

vite 打包库模式 

库模式配置

build配置项

build.lib

build.rollupOptions


当你开发Vue 3应用程序时,有时你可能会需要将自己创建的组件发布到npm上,以便其他开发者可以轻松地在他们的项目中使用这些组件。本文将指导你如何将Vue 3组件发布到npm。
 

准备你的组件

首先,确保你已经创建了你的Vue 3组件,并且它在本地正常工作。你可以使用Vue CLI或手动创建组件,然后使用Vue 3的组件系统进行开发。

创建项目

npm init vite@latest

创建步骤

提示我们要安装create-vite@4.1.0得依赖,选择y

起一个组件名字,然后我们选择vue

这里我选择的是javascript,然后回车

安装完成

因为我们需要了element-ui组件库,所以我们要手动安装一下依赖

npm install element-plus --save

创建组件 

首先,我们要在src\components目录下,创建一个femessage文件夹,

 

在在femessage文件夹下创建el-form-renderer.vue 文件,(自定义名字)


封装我们要得组件

<template>
  <div>
    <el-form ref="myelForm" v-bind="$attrs" :model="value" class="el-form-renderer">
      <template v-for="item in innerContent" :key="item.id">
        <slot :name="`id:${item.id}`" />
        <slot :name="`$id:${item.id}`" />

        <component
          :is="item.type === GROUP ? RenderFormGroup : RenderFormItem"
          :ref="
            (el) => {
              customComponent[item.id] = el;
            }
          "
          :data="item"
          :value="value"
          :item-value="value[item.id]"
          :disabled="
            disabled ||
            (typeof item.disabled === 'function' ? item.disabled(value) : item.disabled)
          "
          :readonly="readonly || item.readonly"
          :options="options[item.id]"
          @updateValue="updateValue"
        />
      </template>
      <slot />
    </el-form>
  </div>
</template>

<script setup>
import RenderFormGroup from "./components/render-form-group.vue";
import RenderFormItem from "./components/render-form-item.vue";
import {
  reactive,
  computed,
  ref,
  watch,
  onMounted,
  nextTick,
  provide,
  getCurrentInstance,
} from "vue";
import transformContent from "./util/transform-content";
import _set from "lodash.set";
import _isequal from "lodash.isequal";
import _clonedeep from "lodash.clonedeep";
import {
  collect,
  mergeValue,
  transformOutputValue,
  transformInputValue,
  correctValue,
} from "./util/utils";
let GROUP = "group";
/**
 * inputFormat 让整个输入机制复杂了很多。value 有以下输入路径:
 * 1. 传入的 form => inputFormat 处理
 * 2. updateForm => inputFormat 处理
 * 3. 但 content 中的 default 没法经过 inputFormat 处理,因为 inputFormat 要接受整个 value 作为参数
 * 4. 组件内部更新 value,不需要走 inputFormat
 */
let value = reactive({}); // 表单数据对象
let options = reactive({});
let initValue = reactive({});
let myelForm = ref();
let methods = {};
const customComponent = ref([]);
let emit = defineEmits(["update:FormData"]);
// 注入 element ui form 方法
/**
 * 与 element 相同,在 mounted 阶段存储 initValue
 * @see https://github.com/ElemeFE/element/blob/6ec5f8e900ff698cf30e9479d692784af836a108/packages/form/src/form-item.vue#L304
 */
onMounted(async () => {
  initValue = _clonedeep(value);
  await nextTick();
  // 检查 myelForm 是否已经初始化
  if (myelForm && myelForm.value) {
    Object.keys(myelForm.value).forEach((item) => {
      // 检查属性是否存在于 methods 对象中
      if (myelForm.value[item] && !(item in methods)) {
        methods[item] = myelForm.value[item];
      }
    });
  }
  /**
   * 有些组件会 created 阶段更新初始值为合法值,这会触发 validate。目前已知的情况有:
   * - el-select 开启 multiple 时,会更新初始值 undefined 为 []
   * @hack
   */
  methods.clearValidate();
});

let props = defineProps({
  //表单项
  content: {
    type: Array,
    required: true,
  },
  // 禁用
  disabled: {
    type: [Boolean, Function],
    default: false,
  },
  //只读
  readonly: {
    type: Boolean,
    default: false,
  },
  /**
   * v-model 的值。传入后会优先使用
   */
  FormData: {
    type: Object,
    default: undefined,
  },
});
//兼容处理
let innerContent = computed(() => transformContent(props.content));
// 初始化默认值
let setValueFromModel = () => {
  if (innerContent.length) return;
  /**
   * 没使用 v-model 时才从 default 采集数据
   * default 值没法考虑 inputFormat
   * 参考 value-format.md 的案例。那种情况下,default 该传什么?
   */
  let newValue = props.FormData
    ? transformInputValue(props.FormData, innerContent.value)
    : collect(innerContent.value, "default");
  correctValue(newValue, innerContent.value);
  if (!_isequal(value, newValue)) value = Object.assign(value, newValue);
};
// v-model初始化默认数据
watch(
  () => props.FormData,
  (newForm) => {
    if (!newForm) return;
    setValueFromModel();
  },
  { immediate: true, deep: true }
);
// 初始化默认数据
watch(
  innerContent,
  (newContent) => {
    try {
      if (!newContent) return;

      // 如果 content 没有变动 remote 的部分,这里需要保留之前 remote 注入的 options
      Object.assign(options, collect(newContent, "options"));
      setValueFromModel();
    } catch (error) {
      console.log(error);
    }
  },
  { immediate: true }
);

// v-model 传递值
watch(value, (newValue, oldValue) => {
  try {
    if (!newValue) return;
    if (props.FormData) {
      let data = Object.assign(
        props.FormData,
        transformOutputValue(newValue, innerContent)
      );
      emit("update:FormData", data);
    }
  } catch (error) {
    console.log(error, "-----");
  }
  // deep: true, // updateValue 是全量更新,所以不用
});

/**
 * 更新表单数据
 * @param  {String} options.id 表单ID
 * @param  {All} options.value 表单数据
 */
let updateValue = ({ id, value: v }) => {
  value[id] = v;
};
/**
 * 重置表单为初始值
 *
 * @public
 */
let resetFields = async () => {
  /**
   * 之所以不用 el-form 的 resetFields 机制,有以下原因:
   * - el-form 的 resetFields 无视 el-form-renderer 的自定义组件
   * - el-form 的 resetFields 不会触发 input & change 事件,无法监听
   * - bug1: https://github.com/FEMessage/el-data-table/issues/176#issuecomment-587280825
   * - bug2:
   *   0. 建议先在监听器 watch.value 里 console.log(v.name, oldV.name)
   *   1. 打开 basic 示例
   *   2. 在 label 为 name 的输入框里输入 1,此时 log:'1' ''
   *   3. 点击 reset 按钮,此时 log 两条数据: '1' '1', '' ''
   *   4. 因为 _isequal(v, oldV),所以没有触发 v-model 更新
   */
  value = _clonedeep(initValue);
  await nextTick();
  methods.clearValidate();
};
/**
 * 当 strict 为 true 时,只返回设置的表单项的值, 过滤掉冗余字段, 更多请看 update-form 示例
 * @param {{strict: Boolean}} 默认 false
 * @return {object} key is item's id, value is item's value
 * @public
 */
let getFormValue = ({ strict = false } = {}) => {
  return transformOutputValue(value, innerContent, { strict });
};
/**
 * update form values
 * @param {object} newValue - key is item's id, value is the new value
 * @public
 */
let updateForm = (newValue) => {
  newValue = transformInputValue(newValue, innerContent);
  mergeValue(value, newValue, innerContent);
};
/**
 * update select options
 * @param {string} id<br>
 * @param {array} options
 * @public
 */
let setOptions = (id, O) => {
  _set(options, id, O);
  options = Object.assign(options); // 设置之前不存在的 options 时需要重新设置响应式更新
};

/**
 * get custom component
 * @param {string} id<br>
 * @public
 */
const getComponentById = (id) => {
  let content = [];
  props.content.forEach((item) => {
    if (item.type === GROUP) {
      const items = item.items.map((formItem) => {
        formItem.groupId = item.id;
        return formItem;
      });
      content.push(...items);
    } else {
      content.push(item);
    }
  });
  const itemContent = content.find((item) => item.id === id);
  if (!itemContent) {
    return undefined;
  }
  if (!itemContent.groupId) {
    return customComponent.value[id].customComponent;
  } else {
    const componentRef = customComponent.value[itemContent.groupId].customComponent;
    return componentRef[`formItem-${id}`].customComponent;
  }
};
provide("methods", methods);
provide("updateForm", updateForm);
provide("setOptions", setOptions);
defineExpose({
  updateValue,
  resetFields,
  getFormValue,
  updateForm,
  setOptions,
  methods,
  getComponentById,
});
</script>
<script>
export default {
  name: "ElFormRenderer",
};
</script>

导出组件 

src 根目录中创建index.js文件,代码如下:

import elFormRenderer from "./components/femessage/el-form-renderer.vue"; // 引入封装好的组件
export { elFormRenderer }; //实现按需引入*
const coms = [elFormRenderer]; // 将来如果有其它组件,都可以写到这个数组里

const components = [elFormRenderer];
const install = function (App, options) {
  components.forEach((component) => {
    App.component(component.name, component);
  });
};
export default { install }; // 批量的引入*

使用vite构建
编辑vite.config.js文件,新增build属性 vite中文文档icon-default.png?t=N7T8https://cn.vitejs.dev/guide/build.html#library-mode

  1. build:这是一个包含构建选项的对象。

  2. lib:这是构建库的选项。

    • entry:指定了库的入口文件,通常是一个 JavaScript 文件的路径。
    • name:定义了库的名称,这里是 "el-form-renderer-vue3"。
    • fileName:定义了输出文件的命名规则,使用了一个函数来生成不同格式的文件名。
  3. rollupOptions:这是用于 Rollup 构建工具的选项,Rollup 通常用于打包 JavaScript 库。

    • external:指定了需要排除的外部依赖项,这里只有 "vue"。

    • output:定义了输出选项。

  • globals:在 UMD 构建模式下,指定了外部依赖的全局变量名,这里将 "vue" 映射到 "Vue",以确保在使用库时可以访问到 Vue.js。

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: path.resolve(__dirname, "src/index.js"),
      name: "el-form-renderer-vue3",
      fileName: (format) => `el-form-renderer-vue3.${format}.js`,
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ["vue"],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: "Vue",
        },
      },
    },
  },
});

修改package.json文件

{
  "name": "el-form-renderer-vue3",
  "version": "1.0.1",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "files": [
    "dist"
  ],
  "main": "./dist/el-form-renderer-vue3.umd.js",
  "module": "./dist/el-form-renderer-vue3.es.js",
  "exports": {
    ".": {
      "import": "./dist/el-form-renderer-vue3.es.js",
      "require": "./dist/el-form-renderer-vue3.umd.js"
    }
  },
  "dependencies": {
    "@element-plus/icons-vue": "^2.1.0",
    "axios": "^1.5.1",
    "element-plus": "^2.3.14",
    "lodash.clonedeep": "^4.5.0",
    "lodash.frompairs": "^4.0.1",
    "lodash.get": "^4.4.2",
    "lodash.has": "^4.5.2",
    "lodash.includes": "^4.3.0",
    "lodash.isequal": "^4.5.0",
    "lodash.isplainobject": "^4.0.6",
    "lodash.kebabcase": "^4.1.1",
    "lodash.set": "^4.3.2",
    "lodash.topairs": "^4.3.0",
    "vue": "^3.3.4",
    "vue-router": "4"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.3",
    "vite": "^4.4.5"
  }
}

 

打包 

当我们都配置好以后,我们就要打包了,这是我们要上传得文件

打包,生成dist文件

npm run build

 

注册npm账号 官网地址

想要发布到npm仓库,就必须要有一个账号,先去npm官网注册一个账号,注意记住用户名、密码和邮箱,发布的时候可能会用到
有些小伙伴可能本地的npm镜像源采用的是淘宝镜像源或者其它的,如果想要发布npm包,我们得吧我们得npm源切换为官方得源,命令如下:
 

npm config set registry=https://registry.npmjs.org

 发布前准备
dist文件生成package.json文件,自定义组件名(唯一,重名报错重新起一个就行),版本号每次上传要高于前一次版本号
dist根目录中运行:

npm init -y
{
  "name": "el-form-renderer-vue3",
  "version": "1.0.2",
  "description": "",
  "main": "el-form-renderer-vue3.es.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

登录npm用户 

npm login
  • Username 用户名
  • Password 密码
  • Email 邮箱地址
  • Enter one-time password 验证码

执行发布命令

npm publish

已经上传成功 

 使用组件

 当我们要在项目中使用的时候就复制 npm i el-form-renderer-vue3

package.json文件中就有了我们安装的组件

这个时候只要像element ui 那样引入就可以全局使用了,在main.js中引入

在我们要用到得.vue中使用 

发布包

创建npm账户

如果你还没有npm账户,你需要先创建一个。前往npm官方网站注册一个账户。

注意:记住你填写的用户名、邮箱、密码,等下你在本地是需要用这些信息登录的。

登录npm

在本地登录你刚刚注册的账号

如果是npm,最后还会给你的邮箱发个验证码,填上之后再回车,才算真正登录成功。

执行 npm login,输入用户名、密码以及你注册时的邮箱。

使用以下命令在终端中登录到你的npm账户:

npm login

 这将提示你输入你的npm用户名、密码和邮箱地址。

切换npm源

因为我们要发布到官方源上面,所以要确保源地址为官方地址 http://registry.npmjs.org 或 https://registry.npmjs.com

可以通过 npm config get registry 命令查看当前 registry 源。

推荐使用nrm管理本地npm源。

全局安装 nrm:  npm install -g nrm

查看npm源:nrm ls
会把所有的源都列出来,其中带*的是当前使用的源

添加npm源:nrm add xxx http://xxxnpm.cn/
xxx 是你给这个源起的名字,后面跟上源的URL 

 删除源:nrm del xxx
xxx 是你给这个源起的名字

发布到npm

最后,运行以下命令来发布你的Vue 3组件到npm:

npm publish

package.json

创建一个Node.js项目时,package.json文件是非常重要的,它包含了项目的配置信息和依赖项。下面是一个典型的package.json文件及其各个字段的介绍:

package.json字段

  • name: 项目的名称,应该是唯一的。
  • version: 项目的版本号,遵循语义化版本规则。
  • description: 项目的简要描述。
  • main: 指定项目的入口文件。
  • scripts: 自定义命令脚本,例如,你可以运行 npm start 启动应用。
  • keywords: 一组关键字,有助于其他人找到你的项目。
  • author: 作者的信息。
  • license: 项目的许可证,常见的包括 MIT、Apache-2.0、GPL-3.0 等。
  • repository: 指定项目的代码仓库信息,包括仓库类型和URL。
  • bugs: 定义问题跟踪系统的URL,以及可选的问题报告邮箱。
  • homepage: 项目的主页URL,通常是项目在代码托管平台上的页面。

接下来是依赖项:

  • dependencies: 生产依赖项,这些包在生产环境中需要。
  • devDependencies: 开发依赖项,这些包在开发和测试过程中需要,但不会包含在生产环境中。
{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "My Node.js Application",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "mocha"
  },
  "keywords": ["node", "javascript"],
  "author": "Your Name",
  "license": "MIT",
  
  "repository": {
    "type": "git",            // 仓库类型
    "url": "https://github.com/yourusername/my-node-app.git" // 仓库 URL
  },
  
  "bugs": {
    "url": "https://github.com/yourusername/my-node-app/issues", // 问题跟踪系统 URL
    "email": "youremail@example.com"  // 可选的问题报告邮箱
  },
  
  "homepage": "https://github.com/yourusername/my-node-app", // 项目主页 URL
  
  "dependencies": {
    "express": "^4.17.1",
    "body-parser": "^1.19.0"
  },
  "devDependencies": {
    "mocha": "^8.0.1",
    "chai": "^4.2.0"
  }
}

要发布一个npm 包,name 和 version 字段是必填的;

包名(name 字段)命名规则:

  • 包名长度不能超过 214 个字符(命名空间也算在里面);
  • 包名所有字符必须小写;
  • 包名可以由连字符 - 组成;
  • 包名不能包含空格,不能以 . 或者 _ 开头,不能包含 ~)('!* 中的任意一个字符;
  • 包名不能包含任何非 url 安全字符(因为包名将作为 url 的一部分);
  • 包名不能与 Node.js / io.js 的核心模块、保留字或黑名单相同,例如 http。

版本号(version 字段)则需要遵循 semver 规范。
 

确定版本号

填写好 package.json 字段后,接下来就是确定我们要发布的版本号,每次对包的更改都应该对应一个版本。

版本号格式

格式:MAJOR.MINOR.PATCH ,值非负整数,且禁止在数字前面补 0

  • MAJOR:主版本号
  • MINOR:次版本号
  • PATCH::修订号

版本号递增逻辑

  • 当有破坏性不兼容的 API 变更时,升级主版本号
  • 当新增一些功能特性时,升级次版本号
  • 当做一些 bug 修复时,升级修订号

当某个版本还不稳定的时候,还可能要先发布一个先行版本,具体可看 semver 规范。

vite 打包库模式 

当你开发面向浏览器的库时,你可能会将大部分时间花在该库的测试/演示页面上。在 Vite 中你可以使用 index.html 获得如丝般顺滑的开发体验。

当这个库要进行发布构建时,请使用 build.lib 配置项,以确保将那些你不想打包进库的依赖进行外部化处理,例如 vue 或 react

库模式配置

// vite.config.js
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    lib: {
      // 也可以是字典或多个入口点的数组
      entry: resolve(__dirname, 'lib/main.js'),
      name: 'MyLib',
      // the proper extensions will be added
      fileName: 'my-lib',
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue',
        },
      },
    },
  },
})

入口文件将包含可以由你的包的用户导入的导出:

// lib/main.js
import Foo from './Foo.vue'
import Bar from './Bar.vue'
export { Foo, Bar }

使用如上配置运行 vite build 时,将会使用一套面向库的 Rollup 预设,并且将为该库提供两种构建格式:es 和 umd (可在 build.lib 中配置):

$ vite build
building for production...
dist/my-lib.js      0.08 KiB / gzip: 0.07 KiB
dist/my-lib.umd.cjs 0.30 KiB / gzip: 0.16 KiB

荐在你库的 package.json 中使用如下格式:

{
  "name": "my-lib",
  "type": "module",
  "files": ["dist"],
  "main": "./dist/my-lib.umd.cjs",
  "module": "./dist/my-lib.js",
  "exports": {
    ".": {
      "import": "./dist/my-lib.js",
      "require": "./dist/my-lib.umd.cjs"
    }
  }
}

或者,如果暴露了多个入口起点:

{
  "name": "my-lib",
  "type": "module",
  "files": ["dist"],
  "main": "./dist/my-lib.cjs",
  "module": "./dist/my-lib.js",
  "exports": {
    ".": {
      "import": "./dist/my-lib.js",
      "require": "./dist/my-lib.cjs"
    },
    "./secondary": {
      "import": "./dist/secondary.js",
      "require": "./dist/secondary.cjs"
    }
  }
}

注意

如果 package.json 不包含 "type": "module",Vite 会生成不同的文件后缀名以兼容 Node.js。.js 会变为 .mjs 而 .cjs 会变为 .js 。

环境变量

在库模式下,所有 import.meta.env.* 用法在构建生产时都会被静态替换。但是,process.env.* 的用法不会被替换,所以你的库的使用者可以动态地更改它。如果不想允许他们这样做,你可以使用 define: { 'process.env.NODE_ENV': '"production"' } 例如静态替换它们。

 

build配置项

build.lib

  • 类型: { entry: string | string[] | { [entryAlias: string]: string }, name?: string, formats?: ('es' | 'cjs' | 'umd' | 'iife')[], fileName?: string | ((format: ModuleFormat, entryName: string) => string) }

构建为库。

  1. entry 是必需的,因为库不能使用 HTML 作为入口。
  2. name 则是暴露的全局变量,并且在 formats 包含 'umd' 或 'iife' 时是必需的。默认 formats 是 ['es', 'umd'],如果使用了多个配置入口,则是 ['es', 'cjs']
  3. fileName 是输出的包文件名,默认 fileName 是 package.json 的 name 选项,同时,它还可以被定义为参数为 format 和 entryAlias 的函数。

build.rollupOptions

  • 类型: RollupOptions

自定义底层的 Rollup 打包配置。这与从 Rollup 配置文件导出的选项相同,并将与 Vite 的内部 Rollup 选项合并。查看 Rollup 选项文档 获取更多细节。

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

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

相关文章

【数据结构】王道强化冲刺课

自用冲刺课复查 考点1 时间与空间复杂度 考点2 线性表 顺序表 链表 考点4567 栈和队列 矩阵 树 图 查找 排序

R实现动态条件相关模型与GARCH模型结合研究中美股市动态相关性(DCC-GARCH模型)

大家好&#xff0c;我是带我去滑雪&#xff01; 中美两国是全球最大的经济体&#xff0c;其经济活动对全球产业链和贸易体系都具有巨大影响。中美之间的经济互动包括大规模的贸易、投资和金融往来。这些互动不仅仅反映在经济数据上&#xff0c;还体现在股市上。中美股市的联动关…

使用任务定时执行软件的定时关机功能,控制电脑可用时间段

目录 定时关机功能可以设置有效的时间段 控制电脑可用时间段的意义 定时执行软件介绍 - 定时执行专家 定时关机设置方法 不可用时间段设置方法 注意事项 总结 在现代社会&#xff0c;电脑已经成为人们生活和工作中不可或缺的一部分。但是&#xff0c;长时间使用电脑也会对…

展会预告丨中国海洋装备博览会盛大开幕!箱讯科技亮相1T18展位

2023年10月12日-15日 中国海洋装备博览会暨2023世界航海装备大会 即将在福州海峡国际会展中心盛大开幕 箱讯科技携手上海虹口区工商联航运商会 亮相本次博览会 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 关于“中国海洋装备博览会” 中国海洋装…

DytanVO 代码复现(服务器端复现rtx3090)

源码地址 代码地址&#xff1a;https://github.com/castacks/DytanVO 环境配置 1.克隆github项目&#xff1a; git clone https://github.com/castacks/DytanVO.git2.利用yaml创建conda 环境&#xff1a; 修改yaml文件 name: dytanvo channels:- pytorch- conda-forge de…

PDM篇 | SOLIDWORKS 2024新功能

改进的视觉内容 优点 重要数据和系统信息一目了然。 • 通过装配体可视化功能&#xff0c;在 SOLIDWORKS 中以图形方式查看零部件数据&#xff0c;如工作流程状态。 • 使用特定图标迅速识别焊件切割清单零部件。 增强的数据保护和跟踪功能 优点 通过附加的审计跟踪信息&am…

Handler-ThreadLocal分析

ThreadLocal 源码分析 在 Android 的 Handler 机制下&#xff0c;ThreadLocal 提供了对不同线程本地变量的副本的保存&#xff0c;并且实现了线程数据的隔离&#xff0c;不同线程的数据不会产生错乱。且在一个线程结束后&#xff0c;其对应在 ThreadLocal 内的数据会被释放&am…

Win10 系统中用户环境变量和系统环境变量是什么作用和区别?

环境&#xff1a; Win10专业版 问题描述&#xff1a; Win10 系统中用户环境变量和系统环境变量是什么作用和区别&#xff1f; 解答&#xff1a; 在Windows 10系统中&#xff0c;用户环境变量和系统环境变量是两个不同的环境变量&#xff0c;它们具有不同的作用和区别 1.用…

数字化转型的环境中,MES管理系统的发展趋势如何

近年来&#xff0c;随着数字化技术的飞速发展&#xff0c;数字化转型以及成为企业发展的必然趋势。在这个过程中&#xff0c;制造业作为国民经济的重要支柱&#xff0c;也面临这前所未有的挑战和机遇。数字化转型下&#xff0c;制造业需要什么样的MES管理系统来应对这些挑战和机…

Domain_audit是一款基于渗透测试角度的域审计工具

关于Domain_audit 该工具是PowerView、Impacket、PowerUpSQL、BloodHound、Ldaprelayscan和Crackmapexec的包装器&#xff0c;用于自动执行枚举和在On-Prem Active Directory渗透测试期间执行的大量检查。 检查项目 Invoke-AD CheckAll将按顺序执行以下操作&#xff1a; 收…

竞赛选题 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

音视频开发岗位,2023年为何持续增加?如何应聘音视频岗位

随着基础设施的完善&#xff08;光纤入户、wifi覆盖、5G普及&#xff09;&#xff0c;加之2020年疫情的影响&#xff0c;将短视频、直播、视频会议、在线教育、在线医疗瞬间推到了顶峰&#xff0c;人们对音视频的需求和要求也越来越强烈。音视频开发是指利用计算机技术和相关编…

殡葬用品商城小程序的作用是什么

随着互联网电商发展&#xff0c;很多东西由线下被搬到了线上&#xff0c;尤其是围绕生活服务的行业更是线上布局经营增长&#xff0c;而随着消费升级&#xff0c;人们购买商品的方式也由以前单一的线上转为线上。 殡葬用品包括寿盒、寿衣、纸钱等产品虽然几乎每个家庭一辈子也…

人物素材的宝藏:10个网站资源推荐

人物素材是设计中应用最广泛的元素之一。无论是网页设计还是移动终端设计&#xff0c;人物素材的插画设计都比文字信息更容易吸引用户的注意力。作为内容呈现&#xff0c;还可以增加设计的艺术属性。为了节省大家寻找人物素材的时间成本&#xff0c;本文立即为大家整理了10个宝…

在CentOS上安装SQL Server,并通过cpolar内网穿透实现数据库的公网访问

文章目录 前言1. 安装sql server2. 局域网测试连接3. 安装cpolar内网穿透4. 将sqlserver映射到公网5. 公网远程连接6.固定连接公网地址7.使用固定公网地址连接 前言 简单几步实现在Linux centos环境下安装部署sql server数据库&#xff0c;并结合cpolar内网穿透工具&#xff0…

焦炭反应性及反应后强度试验方法

声明 本文是学习GB-T 4000-2017 焦炭反应性及反应后强度试验方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了测定焦炭反应性及反应后强度试验方法的原理、试验仪器、设备和材料、试样的采取 和制备、试验步骤、结果的计算及…

Linux上安装Redis教程

本篇文章是基于CentOS7&#xff0c;安装Redis版本为redis-6.2.6。 一、下载并解压Redis 1、执行下面的命令下载redis&#xff1a; wget https://download.redis.io/releases/redis-6.2.6.tar.gz 2、解压redis&#xff1a; tar xzf redis-6.2.6.tar.gz 3、移动redis目录&a…

洗地机哪款最好用?口碑最好的家用洗地机推荐

洗地机方便快捷的清洁方式&#xff0c;如今融入到我们的日常生活需求中来了&#xff0c;然而&#xff0c;在市面上琳琅满目的洗地机品牌中&#xff0c;究竟哪款洗地机比较好用呢&#xff1f;今天&#xff0c;笔者将向大家推荐四款口碑最好的家用洗地机&#xff0c;让你在挑选时…

安全的Sui Move是Web3大规模采用之路的基石

没有信任&#xff0c;就没有Web3的大规模采用。还有其他重要障碍阻碍了首个十亿用户的到来&#xff0c;包括令人困惑的用户体验、复杂的身份验证模式以及不确定的监管体系&#xff0c;但所有障碍中&#xff0c;要数大多数人对区块链技术持怀疑和不信任态度最严重。 对于许多人…

Chrome 118 版本中的新功能

Google Chrome 的最新版本V118正式版 2023/10/10 发布&#xff0c;以下是新版本中的相关新功能供参考。 本文翻译自 New in Chrome 118&#xff0c;作者&#xff1a; Adriana Jara&#xff0c; 略有删改。 以下是主要内容&#xff1a; 使用scope css规则在组件中指定特定样式。…