前端工程化之手搓webpack5 --【elpis全栈项目】

前端工程化之手搓webpack5 --【elpis全栈项目】

导读



基本流程:输入 – 编译 – 输出

被引擎读取
打包
输入--业务文件:pages
编译--引擎编译
输出--产物文件:dist


我们要做的就是:

  1. 配置引擎自动读取业务文件夹pages(比如一些xxx.vue文件);
  2. 对业务代码进行编译、分包、压缩等操作;
  3. 得到一个dist文件夹(包含html\css\js等)

最后将dist部署到服务器就行了,这应该是任何一个工程化工具实现的最基本逻辑了吧。

所以,以webpack为例我们有必要认识一些基本配置属性:
在这里插入图片描述

以我的项目为例,我们来一步步配置出一个适合自己的webpack脚手架。以下是目录:

以下目录都在app文件夹下:

|-- pages 				业务文件夹: 存放vue等文件
|-- public				产出文件夹: 打包后输出的dist文件会生成在这里
|-- webpack				
	|-- config			webpack不同环境配置
		|-- webpack.base.js
		|-- webapck.dev.js
		|-- webapck.prod.js
	|-- dev.js      	启动 开发 环境的的入口文件
	|-- prod.js			启动 生产 环境的的入口文件
  • 这是我的pages文件夹,用以实现一个项目多页面入口;(这个js就简单的认为他是个一般的SPA项目的mian.js
    在这里插入图片描述
  • 最后prod环境要实现这样一个效果:将代码分割、分包、压缩、提取公共方法、树摇…等等。在这里插入图片描述
  • dev环境要实现这样一个效果:本地起一个服务,将dist放到本地的服务器上、实现HRM(热更新)在这里插入图片描述

一、 基本配置: webpack.base.js


我们将公共配置,集中提取放置到这个base文件中,避免重复配置。
基本思路:

  1. entry:配置入口文件的路径
const pageEntries = {};
// 生成一个绝对路径,例如:/user/elpis/app/pages/**/entry.*.js
const entryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js');
// glob.sync是一个同步方法,会返回一个包含所有匹配文件路径的数组。
glob.sync(entryList).forEach(file => {
  // file会输出:'/user/elpis/app/pages/page1/entry.page1.js'
  const entryName = path.basename(file, '.js');  // 会提取文件名并去掉 .js, 例如:entry.page1
  pageEntries[entryName] = file;
})

module.exports = {
	// entry: {entry.page1: '/user/elpis/app/pages/page1/entry.page1.js', ...}
	entry: pageEntries  
}
  1. module:规定不同的文件,分别需要用什么loader去解析。用test属性配置文件的匹配规则;用include指定范围;用use指定使用哪个loaderoptions则是对loader的配置。
 module.exports ={
 	...
 	module: {
	    rules: [
	     {
	      test: /\.css$/,
	      use: ['style-loader', 'css-loader']
	    },
	    {
	      test: /\.js$/,
	      include: [ path.resolve(process.cwd(), '/app/pages')], // 只对业务代码进行babel,加快打包速度
	      use: { loader: 'babel-loader' }
	    },
	    {
	      test: /\.(png|jpe?g|gif)(\?.+)?$/,
	      use: { 
	        loader: 'url-loader',
	        options: {limit: 300,  esMoule: false }   // 小于300kb的图片会被转成base64编码、禁用esModule
	      }
	    }
	  ]
	}
	...
 }
  1. resolve: 配置一些解析时候的具体行为,是个优化项,看个人需要配置。例如
module.exports ={
  ...
  resolve: {
    // 定义别名,方便引入业务代码: import { xxx } from '$common/xxx';
    alias: { $common: path.resolve(process.cwd(), './app/pages/common') }
  }
  ...
}
  1. plugins: 使用插件。
module.exports ={
  ...
  plugins: [
    new VueLoaderPlugin()  // 处理 .vue 文件,这个插件是必须的
    ...
  ]
  ...
}
  1. optimization: 输出优化。代码分割,模块分割,缓存,treeShaing,压缩等优化策略
module.exports ={
  ...
  optimization: {
  	splitChunks:{...}, // 代码分割, 具体的看下文
    runtimeChunk: true 	// 将 webpack 运行时生成的代码打包到 runtime.js
  }
  ...
}

由此我们得到了一个完整的 base.js 的配置:

const glob = require('glob')
const path = require('path')
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')

const pageEntries = {};
const htmlWebpackPluginList = [];

// 获取 app/pages 目录下的所有页面入口文件(entry.xx.js)
const entryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js');
glob.sync(entryList).forEach(file => {
  const entryName = path.basename(file, '.js');
  // 构造 entry
  pageEntries[entryName] = file;
  // 构造最终渲染的页面文件
  htmlWebpackPluginList.push(new HtmlWebpackPlugin({
    // 产物最终模版
    filename: path.resolve(process.cwd(), './app/public/dist/', `${entryName}.tpl`),
    // 指定模版文件
    template: path.resolve(process.cwd(), './app/view/entry.tpl'),
    // 要注入的代码块
    chunks: [entryName]
  }))
})

/**
 * webpack 基础配置
 */
module.exports = {
  // 入口配置
  entry: pageEntries,
  // 模块解析配置
  module: {
    rules: [{
      test: /\.vue$/,
      use: {
        loader: 'vue-loader'
      }
    },
    {
      test: /\.js$/,
      include: [
        path.resolve(process.cwd(), '/app/pages') // 只对业务代码进行babel,加快打包速度
      ],
      use: {
        loader: 'babel-loader'
      }
    },
    {
      test: /\.(png|jpe?g|gif)(\?.+)?$/,
      use: {
        loader: 'url-loader',
        options: {
          limit: 300, // 小于300kb的图片会被转成base64编码
          esMoule: false // 禁用esModule
        }
      }
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader']
    }, {
      test: /\.less$/,
      use: ['style-loader', 'css-loader', 'less-loader']
    },
    {
      test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/, // 例如:file.woff2?v=abc&opt=1
      use: 'file-loader'
    }
    ]
  },
  // 产物输出路径, 因为开发和生产环境输出不一致,所以在各自环境中进行配置
  output: {},
  // 配置模块解析的具体行为(定义 webpack 在打包时,如何找到并解析具体模块的路径)
  resolve: {
    // 尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀
    // 能够使用户在引入模块时不带扩展:import File from '../path/to/file';
    extensions: ['.js', '.vue', '.less', '.css'],
    // 配置别名: import { xxx } from '$common/xxx';
    alias: {
      $page: path.resolve(process.cwd(), './app/pages'), // 定义别名,方便引入业务代码
      $common: path.resolve(process.cwd(), './app/pages/common'),
      $widgets: path.resolve(process.cwd(), './app/pages/widgets'),
      $store: path.resolve(process.cwd(), './app/pages/store'),
    }
  },
  // 配置 webpack 插件
  plugins: [
    // 处理 .vue 文件,这个插件是必须de
    // 它的职能是将定义过的其他规则复制并应用到 .vue 文件中
    // 例如,如果有一条匹配规则 /\.js$/ 的规则, 那么他会应用到 .vue 文件中的 script 板块中
    new VueLoaderPlugin(),
    // 把第三方库暴露到 window context 下 
    // 任何文件都可以直接使用 Vue,Webpack 会自动将其映射为 require('vue')。
    // 例如 new Vue( { el: '#app', render: h => h(App) } );
    new webpack.ProvidePlugin({ Vue: 'vue' }),
    // 定义全局常量
    new webpack.DefinePlugin({
      __VUE_OPRIONS_API__: 'true', // 禁用选项式 API 支持
      __VUE_PRO_DEVTOOLS: 'false', // 禁用 vue 调试工具
      __VUE_PRO_HYDRATION_MISMATCH_DETAILS__: 'false' // 禁用生产环境构建下激活 (hydration) 不匹配的详细警告
    }),
    // 显示打包进度
    new webpack.ProgressPlugin(),
    // 每次 build 前清空 public/dist 目录
    new CleanWebpackPlugin(['public/dist'], {
      root: path.resolve(process.cwd(), './app/'),
      exclude: [],
      verbose: true,
      dry: false
    }),
    // 构造最终渲染的页面模版
    ...htmlWebpackPluginList,
  ],
  // 配置打包输出优化(代码分割,模块分割,缓存,treeShaing,压缩等优化策略)
  optimization: {
    /**
     * 把 js 文件打包成3种类型
     * 1. verdor: 第三方 lib 库, 基本不会改动, 除非依赖版本升级
     * 2. common: 业务组件代码的公共部分抽取出来, 改动较少
     * 3. ebnty.{page}:  不同页面 entry 里的业务组件代码的差异部分,会经常改动
     * 目的: 把改动和引用频率不一样的 js 区分出来,已达到更好利用浏览器缓存的效果
     */
    splitChunks: {
      chunks: 'all', // 对同步和异步模块都进行分割
      maxAsyncRequests: 10, // 每次异步加载的最大并行请求数
      maxInitialRequests: 10, // 入口点的最大并行请求数
      cacheGroups: {
        vendor: { // 第三方库
          test: /[\\/]node_modules[\\/]/, // 打包node_modules 目录下的模块
          name: 'vendor', //模块名称
          priority: 20, // 优先级,数字越大越优先
          enforce: true, // 强制执行
          reuseExistingChunk: true, // 复用已有的公共 chunk
        },
        common: { // 业务组件公共代码
          name: 'common',
          minChunks: 2, // 被两处引用即被归为公共模块
          minSize: 1024 * 1, // 最小分割文件大小 
          priority: 10, // 优先级
          reuseExistingChunk: true, // 复用已有的公共 chunk
        }
      },
    },
    // 将 webpack 运行时生成的代码打包到 runtime.js
    runtimeChunk: true
  },
}

二、 生产环境配置:webpack.prod.js + prod.js


生产环境的配置主要集中在打包优化上,比如:代码分割、压缩、分包、树摇等等。
基本思路:

  1. 基于base.js,合并配置
const baseConfig = require('./webpack.base.js');
const webpackConfig = merge.smart(baseConfig, {
	mode:'',
	output:''
	...
})
  1. mode:指定模式,为生产环境。指定生产环境之后会默认开启一些配置,比如 tree sharking。
mode:'production'
  1. output: 配置产物输出路径。属性path 指定的是 Webpack 打包后文件输出的物理路径, 属性publicPath 指定的文件在服务器上的访问路径
 output: {
    filename: 'js/[name]_[chunkhash:8].bundle.js', // 具体看 文档output/#template-strings
    path: path.join(process.cwd(), './app/public/dist/prod'),
    publicPath: '/dist/prod', // 输出目录的公共 URL
    crossOriginLoading: 'anonymous' // 允许跨域加载
 }
  1. module: 对每个模块要使用的loader等配置。使用thread-loader实现多线程打包
module: {
    rules: [
	{
      test: /\.js$/,
      include: [path.resolve(process.cwd(), './app/pages')],
      use: [{
        loader: 'thread-loader', // 多线程编译loader
        options: {
          workers: os.cpus().length, // 使用 CPU 核心数
        }
      },
      {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env'], // 用于语法转换和按需引入 polyfill (处理旧版本浏览器兼容,填补 API/新特性 缺失)
          plugins: ['@babel/plugin-transform-runtime'] // 用于复用辅助代码和模块化 polyfill。
        }
      }
      ]
    }]
  }
  1. plugins: 插件配置。使用MiniCssExtractPlugin插件,提取css公共部分等等。
  2. optimization: 优化配置。使用TerserWebpackPlugin提升压缩阶段的性能
  3. 最后将webpack.prod.js的配置传入prod.js,prod.js通过webpack()方法启动打包
const webpackProdConfig = require('./config/webpack.prod.js');
webpack(webpackProdConfig, (err, stats) => {...做一些执行打包的回调处理}))

生产环境全量配置:wbepack.prod.js

const path = require('path');
const merge = require('webpack-merge');
const os = require('os');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CSSMinimizerPlugin = require('css-minimizer-webpack-plugin')
const HtmlWebpackInjectAttributesPlugin = require('html-webpack-inject-attributes-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')

// 基类配置
const baseConfig = require('./webpack.base.js');

const webpackConfig = merge.smart(baseConfig, {
  // 指定生产环境
  mode: 'production',
  // 生产环境的 out put 配置
  output: {
    filename: 'js/[name]_[chunkhash:8].bundle.js',
    path: path.join(process.cwd(), './app/public/dist/prod'),
    publicPath: '/dist/prod', // 输出目录的公共 URL
    crossOriginLoading: 'anonymous' // 允许跨域加载
  },
  module: {
    rules: [{
      test: /\.css$/,
      use: [MiniCssExtractPlugin.loader, "thread-loader"]
    }, {
      test: /\.js$/,
      include: [path.resolve(process.cwd(), './app/pages')],
      use: [{
        loader: 'thread-loader', // 多线程编译loader
        options: {
          workers: os.cpus().length, // 使用 CPU 核心数
        }
      },
      {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env'], // 用于语法转换和按需引入 polyfill (处理旧版本浏览器兼容,填补 API/新特性 缺失)
          plugins: ['@babel/plugin-transform-runtime'] // 用于复用辅助代码和模块化 polyfill。
        }
      }
      ]
    }]
  },
  // performance 用于控制性能提示信息, 默认为 warning; 文件体积过大, 入口过多, 资源加载方式等情况下会提示警告
  performance: {
    hints: false
  },
  plugins: [
    // 提取 css 的公共部分, 有效利用缓存
    new MiniCssExtractPlugin({
      chunkFilename: 'css/[name]_[contenthash:8].bundle.css'
    }),
    // 优化并压缩 css
    new CSSMinimizerPlugin(),
    // 浏览器在请求资源时不发送用户的身份凭证
    new HtmlWebpackInjectAttributesPlugin({
      crossorgin: 'anonymous'
    })
  ],
  optimization: { // 优化配置
    // 使用 TerserWebpackPlugin 的并发和缓存,提升压缩阶段的性能
    minimize: true,
    minimizer: [new TerserWebpackPlugin({
      cache: true, // 启用缓存来加速构建过程
      parallel: true, // 利用多核 CPU 并行压缩
      terserOptions: {
        compress: {
          drop_console: true // 移除 console.log
        }
      }
    })]
  }
});

module.exports = webpackConfig;

生产环境启动配置:prod.js

const webpack = require('webpack');
const webpackProdConfig = require('./config/webpack.prod.js');

console.log('\nbuilding... \n');


// 如果你不向 webpack 传入可执行的回调函数, 它会返回一个 webpack Compiler 实例并在其中进行操作
// const compiler = webpack(webpackProdConfig);
// 区别在于compiler.run()更具灵活性、控制粒度等适合多次打包, 直接传入一个回调函数 (err, stats)=>{} 则只会执行一次打包, 更适用于生存环境的场景
webpack(webpackProdConfig, (err, stats) => {
  // 配置文件错误
  if (err) {
    console.log('❗err: \n', err)
    return
  }

  // stats.hasErrors()判断缺失的 module,语法错误等, 还有个 stats.hasWarnings() 方法,可以用来判断是否有警告信息
  if (stats.hasErrors()) {
    const info = stats.toJson()
    console.error(info.errors);
  }

  // process.stdout.write 更高效,适合大量数据输出 (console.log 是基于它实现的)
  process.stdout.write(`${stats.toString({
    colors: true,// 在控制台输出色彩信息
    modules: false, // 不显示每个模块的打包信息
    children: false, // 不显示子编译任务的信息
    chunks: false, // 不显示每个代码块的信息
    chunkModules: false // 显示代码块中模块的信息
  })}\n`)
});


三、 开发环境配置:webpack.dev.js + dev.js


开发环境则需要,配置 HMR 实现热更新

  1. 通过merge合并base和dev的配置
 const webpackConfig = merge.smart(baseConfig,{
	mode:'',
	....
})
  1. 通过修改base.js的entry来配置HRM
const baseConfig = require('./webpack.base.js');

// 开发阶段的 entry 配置需要加入 hmr
Object.keys(baseConfig.entry).forEach(v => {
  // 第三方包不作为 hmr 的入口
  if (v !== 'vendor') {
    baseConfig.entry[v] = [
      baseConfig.entry[v], // 主入口文件
      // hmr 更新入口,官方指定的 hmr 路径
      `webpack-hot-middleware/client?path=http://${host}:${port}/${hmrPath}&timeout=${timeout}`,
    ]
  }
})
  1. 指定mode 等于 ’development‘
  2. devtool: 等于 eval-cheap-module-source-map时。 soure-map 配置 便于开发时调试
  3. output: 配置产物输出路径。与生产环境不同的是,开发环境的产物需要放到本地服务器上。通过设置 globalObject: 'this', Webpack 会根据运行环境自动选择正确的全局对象。
 output: {
    filename: 'js/[name]_[chunkhash:8].bundle.js',
    path: path.resolve(process.cwd(), './app/public/dist/dev/'),
    publicPath: `http://${host}:${port}/public/dist/dev/`, // 输出目录的公共 URL
    globalObject: 'this'
  }
  1. plugins: 通过配置webpack.HotModuleReplacementPlugin插件实现热模块替换
 plugins: [ new webpack.HotModuleReplacementPlugin({ multiStep: false }) ]
  1. 自定义一个服务,将产物文件放到本地服务器上。主要是用到两个中间件: 用devMiddleware监控文件改动,用hotMiddleware实现热更新,通知浏览器刷新
const { webpackConfig, DEV_SERVER_CONFIG } = require('./config/webpack.dev');
const app = express();
const compiler = webpack(webpackConfig)

app.use(devMiddleware(compiler, {}))
app.use(hotMiddleware(compiler, {}))
app.listen(DEV_SERVER_CONFIG.PORT, () => {})

开发环境打包配置:webpack.dev.js

const path = require('path');
const merge = require('webpack-merge');
const webpack = require('webpack')

// 基类配置
const baseConfig = require('./webpack.base.js');

// dev-server 配置
const DEV_SERVER_CONFIG = {
  HOST: '127.0.0.1',
  PORT: 9200,
  HMR_PATH: '__webpack_hmr',
  TIMEOUT: 20000,
}
const { HOST: host, PORT: port, HMR_PATH: hmrPath, TIMEOUT: timeout } = DEV_SERVER_CONFIG;

// 开发阶段的 entry 配置需要加入 hmr 
Object.keys(baseConfig.entry).forEach(v => {
  console.log('--------v-------', v)
  // 第三方包不作为 hmr 的入口  有时候可能会手动配置第三方库 entry: { vendor: ['vue', 'lodash']} 将其打包到一个单独的文件中
  // 与 splitChunks 的区别: 自动从 node_modules 中提取第三方库。更灵活,适用于复杂的项目。
  if (v !== 'vendor') {
    baseConfig.entry[v] = [
      baseConfig.entry[v], // 主入口文件
      // hmr 更新入口,官方指定的 hmr 路径
      `webpack-hot-middleware/client?path=http://${host}:${port}/${hmrPath}&timeout=${timeout}`,
    ]
  }
})

const webpackConfig = merge.smart(baseConfig, {
  // 指定开发环境
  mode: 'development',
  // soure-map 配置 便于开发时调试
  devtool: 'eval-cheap-module-source-map',
  // 开发环境的 out put 配置
  output: {
    filename: 'js/[name]_[chunkhash:8].bundle.js',
    path: path.resolve(process.cwd(), './app/public/dist/dev/'),
    publicPath: `http://${host}:${port}/public/dist/dev/`, // 输出目录的公共 URL
    globalObject: 'this' // 用于指定 Webpack 打包代码时引用的全局对象。配置成 'this' Webpack 会根据运行环境自动选择正确的全局对象。
  },
  // 开发阶段插件
  plugins: [
    // HotModuleReplacementPlugin 用于实现热模块替换 (Hot Module Replacement - HMR)
    // 模块热替换允许在应用程序运行时替换模块
    // 极大的提升开发效率, 因为能让应用程序一直保持运行状态
    new webpack.HotModuleReplacementPlugin({ multiStep: false }),
  ]
});

module.exports = {
  webpackConfig,  // webpack 配置
  DEV_SERVER_CONFIG // devServer 配置, 暴露给dev.js使用
};

开发环境启动配置:dev.js

// 本地开发启动devServer
const express = require('express');
const path = require('path');
const webpack = require('webpack');
const devMiddleware = require('webpack-dev-middleware');
const hotMiddleware = require('webpack-hot-middleware');

// 从 webpack.dev.js 获取 webpack 配置 和 devServer 配置
const { webpackConfig, DEV_SERVER_CONFIG } = require('./config/webpack.dev');

const app = express();

const compiler = webpack(webpackConfig)

// 指定静态文件目录
app.use(express.static(path.join(__dirname, '../public/dist')));

// 引用 devMiddleware 中间件 (监控文件改动)
app.use(devMiddleware(compiler, {
  writeToDisk: (filPath) => filPath.endsWith('.tpl'), // 落地文件
  publicPath: webpackConfig.output.publicPath, // 资源路径
  // headers 配置
  headers: {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
    'Access-Control-Allow-Headers': 'X-Requested-With, contnet-type, Authorization'
  },
  stats: {
    colors: true
  }
}))

// 引用 hotMiddleware 中间件 (热更新)
app.use(hotMiddleware(compiler, {
  path: `/${DEV_SERVER_CONFIG.HMR_PATH}`,
  log: () => { }
}))

console.info('请等待webpack初次构建完成...')

const port = DEV_SERVER_CONFIG.PORT;
app.listen(port, () => {
  console.log("🚀 ~ app.listening on port:", port)
})

四、 配置npm启动打包


pagkage.json:

在这里插入图片描述

因为开发环境下,产物文件都放在本地服务器上,所以需要通过配置--max_old_space_size分配好足够的内存。

    "build:dev": "node --max_old_space_size=4096 ./app/webpack/dev.js",
    "build:prod": "node ./app/webpack/prod.js"

更多参考:wbepack中文文档

全文特别鸣谢: 抖音“哲玄前端”,《全栈实践课》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

云备份项目--服务端编写

文章目录 7. 数据管理模块7.1 如何设计7.2 完整的类 8. 热点管理8.1 如何设计8.2 完整的类 9. 业务处理模块9.1 如何设计9.2 完整的类9.3 测试9.3.1 测试展示功能 完整的代码–gitee链接 7. 数据管理模块 TODO: 读写锁?普通锁? 7.1 如何设计 需要管理…

深入了解 ES6 Map:用法与实践

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

大润发易主,被阿里割肉卖了

文丨白念云 零售行业2025年伊始便迎来一则重磅消息:大润发被卖了。 1月1日晚,阿里巴巴集团发布公告,宣布子公司及NewRetail与德弘资本达成交易,以最高约131.38亿港元出售所持高鑫零售(大润发母公司)全部股…

VulnHub—potato-suncs

使用命令扫描靶机ip arp-scan -l 尝试访问一下ip 发现一个大土豆没什么用 尝试扫描一下子域名 没有发现什么有用的信息 尝试扫描端口 namp -A 192.168.19.137 -p- 尝试访问一下端口,发现都访问不进去 查看源代码发现了网页的标题 potato,就想着爆破一下密码 hydr…

docker学习记录:commit,制作自己的镜像

1.清除所有 ktkt-SYS-4028GR-TR2:~$ sudo docker rm -f $(sudo docker ps -aq)2.再操作一次tomcat,修改好,再打成一外镜像 ktkt-SYS-4028GR-TR2:~$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE tomcat 9.0 3…

macos安装java8

下载 dmg方式安装 安装 双击pkg运行 输入java -version验证 配置环境变量 cd ~ ls -a输入 ls -a后查看是否已经存在.bash_profile文件,如果已经存在就不需要创建,如果不存在,继续执行下方命令创建文件 touch .bash_profile /usr/l…

【每日学点鸿蒙知识】自定义键盘光标、Cavas绘制、XComponent触发键盘抬起等

【每日学点鸿蒙知识】24.08.25 【每日学点鸿蒙知识】自定义键盘光标、Cavas绘制、XComponent触发键盘抬起等 1、基于自定义键盘如何设置光标位置? 可以参考如下代码: class MyKeyboardController {public onInputChanged?: (value: string) > vo…

在Mysql环境下对数据进行增删改查

一、插入数据: insert into 表名 [(字段名)] values (字段对应的值1,字段对应的值2,…)[,(字段对应的值1,字段对应的值2,…)]; insert into students (id,name,age,height,gender,cls_id,is_delete) values (0,小明,18,180.00,2,1,0)在学生表中插入“小明”数据的…

Web网页制作之JavaScript的应用

---------------📡🔍K学啦 更多学习资料📕 免费获取--------------- 实现的功能:1.通过登录界面跳转至主页面,用户名统一为“admin”,密码统一为“admin123”,密码可显示或隐藏,输入…

Markdown编辑器——Typora(Picgo+Github图床)

Markdown编辑器——Typora(PicgoGithub图床) 文章目录 Markdown编辑器——Typora(PicgoGithub图床)安装Typora安装PicGoPicGo软件下载PicGo的npm版本下载 GitHub图床配置PicGo配置PicGo的软件配置PicGo的npm版本信息配置 配置Typo…

Unity 3D游戏开发从入门进阶到高级

本文精心整理了Unity3D游戏开发相关的学习资料,涵盖入门、进阶、性能优化、面试和书籍等多个维度,旨在为Unity开发者提供全方位、高含金量的学习指南.欢迎收藏。 学习社区 Unity3D开发者 这是一个专注于Unity引擎的开发者社区,汇聚了众多Un…

Python 21:Debug

1. Debug的作用 当程序的预期结果和实际结果不一致时,可以用Debug模式进行调试来定位问题的位置。 2. Debug使用 1)设置断点 点击行号,出现”断点“ 2)执行Debug 点击Debug 或者右键,点击debug进入debug模式 3.Debu…

(CICD)自动化构建打包、部署(Jenkins + maven+ gitlab+tomcat)

一、平滑发布与灰度发布 **什么叫平滑:**在发布的过程中不影响用户的使用,系统不会因发布而暂停对外服务,不会造成用户短暂性无法访问; **什么叫灰度:**发布后让部分用户使用新版本,其它用户使用旧版本&am…

强化学习入门谈

之前我们见识到很多机器学习大展手脚的任务场景了,但是机器学习依旧有很多软肋。 回忆一下,我们之前做的机器学习(深度学习)策略基本都是类似于"supervised learning"的方法,比如你想用CNN实现一个classifi…

colnames看似简单,却能优化数据处理流程

引言 在数据处理和分析中,变量名称是至关重要的,它们决定了数据的可读性和操作的简便性。在R语言中,colnames 函数以其简单的语法设计,提供了高效管理数据框列名的能力,尤其是在复杂的爬虫任务中显得尤为重要。本篇文…

【分布式】Hadoop完全分布式的搭建(零基础)

Hadoop完全分布式的搭建 环境准备: (1)VMware Workstation Pro17(其他也可) (2)Centos7 (3)FinalShell (一)模型机配置 0****)安…

ArcGIS中怎么把数据提取到指定范围(裁剪、掩膜提取)

最近,经常能收到怎么把数据提取到指定范围、栅格数据怎么裁剪、矢量数据怎么裁剪、栅格数据怎么掩膜提取的咨询。 下面是我对这个问题的解决思路: 对于矢量数据: ①首先把数据加载进来 ②软件界面上面的工具栏找到→地理处理→裁剪&#x…

intra-mart环境搭建笔记

一、前言 最近在做intra-mart项目,网上这些笔记比较少,在此做一下笔记。 intra-mart是由日本intra-mart公司开发和销售的工作流平台,国内确实不怎么用,日本企业用的多些,面试时会问有没有intra-mart经验。 这个自学…

智能型电瓶车充电桩在老居民区充电站中的应用优势

摘要 随着电瓶车数量的快速增长,小区内的电瓶车充电需求日益增加,但传统充电方式存在诸多安全隐患。电瓶车智能充电桩作为一种新型充电解决方案,能够有效解决充电难题,并提升充电安全性和便捷性。本文以ACX10A型电瓶车充电桩为…

生产看板真的有用吗?

​看板,对于从事制造行业的人员来说,这并不陌生。但是对于看板起到的作用,那可就是众说纷纭,有人说,看板是领导的“面子工程”,是混淆上级视察的工具;也有人说,看板真切地帮助车间提…