• 对vite的简单了解


    一.什么是构建工具

    浏览器只认识html,css,js。

    构建工具做了那些:

    1.模块化开发支持:支持直接从node_modules里引入代码+多种模块化支持

    2. 处理代码兼容性:比如Babel语法降级,less,ts语法转换(不是构建工具做,构建工具将这些语法对应的处理工具集成进来自动化处理

    3.提高项目性能:压缩文件,代码分割

    4.优化开发体验:

    构建工具会自动监听文件的变化,当文件变化以后自动帮你调用对应的集成工具进行重新打包,然后再次浏览器重新运行(整个过程叫做热更新, hot )

    开发服务器:跨域的问题,用vue-cli react-cli 解决跨域的问题(dev代理)

    总结:构建工具让我们可以不用每次关心代码在浏览器重如何运行

    二.vite介绍

    (一)什么是vite

    vite 是一种新型的前端构建工具,能够显著的提升前端开发者的体验。它主要有俩部分组成:

    一个开发服务器:它基于原生的es模块,提供了丰富的内建功能,如速度快到惊人的模块热更新HMR.

    一套构建指令:使用Rollup打包代码,并且它是预构建的,可输出用于生产环境的高度优化过的静态资源。

    vite旨在提供开箱即用的配置,但同时它也提供插件API和JavaSccript API 带来高度的可扩展性并且有完整的类型支持。

    (二)为什么选vite?

    主要因为俩方面缓慢的服务器启动和缓慢的更新。具体查看官网

    总结:

    • vite解决打包问题:vite只启动一台静态页面的服务器,对文件代码不打包,服务器会根据客户端的请求加载不同的模块处理,实现真正的按需加载
    • vite解决热更新问题:Vite采用立即编译当前修改文件的办法,同时vite还会使用缓存机制(http缓存=>vite内置缓存),加载更新后的文件内容,所以,vite具有了快速冷启动、按需编译、模块热更新等优良特质(Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。)
    • vite基于缓存的热更新,vue-cli基于webpack的热更新

    三.vite与webapck比较

    1.vite启动时间快:
    webpack先打包,再启动开发服务器,请求服务器时直接给予打包后的结果
    vite 直接启动开发服务器(根据服务器支持 es module,把压力给了服务器),请求哪个模块再对哪个模块进行实时编译

    2. 机制不一样:
    原理:webpack:逐级递归识别依赖,构建依赖图谱->转化AST语法树->处理代码->转换为浏览器可识别的代码
    vite:基于浏览器原生 ES module,利用浏览器解析 imports,服务器端按需编译返回
    3.生态不及webpack,加载器、插件不够丰富
    4.生产环境esbuild构建对于css和代码分割不够友好
    静态资源图片压缩,还有文件打包做回滚,涉及到babel-plugins就有问题
    5.vite 打包实际就是 rollup打包

    四.理解vite脚手架和vite的区别

    官网搭建vite:

     yarn create vite

    其实是全局安装一个create-vite(vite的脚手架),而不是vite(vite 就是一个简单的构建工具)

    create-vite 与vite的关系是?——》create-vite内置了vite。

    五. 依赖预构建

     (一) 依赖预构建的目的

    为了兼容 CommonJS 和 UMD(开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此,Vite 必须先将作为 CommonJS 或 UMD 发布的依赖项转换为 ESM);

    以及提升性能(如:lodash-es 有超过 600 个内置模块!浏览器会同时发出600多个http请求,尽管服务器端处理这些请求的时候是没有问题的,但是大量的请求会在浏览器端造成网络拥塞,导致页面的加载速度相当的慢,通过vite预构建,加载成一个模块,浏览器只要做一次http请求就够了)

    (二)自动依赖搜寻

            默认情况下,在模块预构建完成以后,对构建好的模块会进行缓存;如果没有找到相应的缓存,Vite 将抓取你的源码,并自动寻找引入的依赖项,并将这些依赖项作为预构建包的入口点。预构建通过 esbuild 执行,所以它通常非常快。

            在服务器已经启动之后,如果遇到一个新的依赖关系导入,而这个依赖关系还没有在缓存中,Vite 将重新运行依赖构建进程并重新加载页面。

    (三)需要预构建的模块

    1.  只有 bare import 会执行依赖预构建

    •  bare import:一般是 npm 安装的模块,是第三方的模块,不是我们自己写的代码,一般情况下是不会被修改的,因此对这部分的模块提前执行构建并且进行缓存,有利于提升性能。
    • monorepo下的模块不会被预构建,部分模块虽然是 bare import,但这些模块也是开发者自己写的,不是第三方模块,因此 Vite 没有对该部分的模块执行预构建。
    1. // vue 是 bare import
    2. import vue from "vue"
    3. import xxx from "vue/xxx"
    4. // 用路径去访问的模块,不是 bare import
    5. import foo from "./foo.ts"
    6. import foo1 from "/foo.ts"

     2. vite的判断

    • 实际路径在 node_modules 的模块会被预构建,这是第三方模块
    • 实际路径不在 node_modules 的模块,证明该模块是通过文件链接,链接到 node_modules 内的(monorepo 的实现方式),是开发者自己写的代码,不执行预构建

    (四)缓存

    vite缓存分2部分:

    A. 文件系统缓存:Vite 会将预构建的依赖缓存到 node_modules/.vite.

    • package.json 中的 dependencies 列表
    • 包管理器的 lockfile,例如 package-lock.jsonyarn.lock,或者 pnpm-lock.yaml
    • 可能在 vite.config.js 相关字段中配置过的 

    以上这个文件发生变化,vite就会重新执行预构建。你想要强制 Vite 重新构建依赖,你可以用 --force 命令行选项启动开发服务器,或者手动删除 node_modules/.vite 目录。 

    "scripts": {

        "dev": "vite --force"

      },

    B. 浏览器缓存:解析后的依赖请求会以 HTTP 头 max-age=31536000,immutable 强缓存,以提高在开发时的页面重载性能。一旦被缓存,这些请求将永远不会再到达开发服务器。 

    • 如果安装了不同的版本
    • 通过本地编辑来调试依赖项

    以上2种方式浏览器缓存则失效。 

    (五)过程

    首先vite会找到对应的依赖,然后调用esbuild( 对js 语法进行处理的一个库)将其他规范的代码(如common.js 规范的:module.exports形式导出的)转换成esmodule规范,然后放到当前目录下的/node_modules/.vite/deps(Vite 会将预构建的依赖缓存到 node_modules/.vite),同时对esmodule规范的各个模块进行统一集成。

    他解决了3个问题:

    1. 不同的第三方包会有不同的导出格式(这个是vite没法约束人家的事情)

    如:common.js 规范 

    2. 对路径的处理上可以直接使用.vite/deps,方便路径重写(解决路径冲突问题)

    3. 叫做网络多包传输性能问题(这也是原生esmodule规范不敢支持node_modules的原因之一),有了依赖预构建以后无论他有多少的额外exports 和import,vite都会尽可能将他们进行集成最后只生产一个或者几个模块

    如安装lodash-es,引入:import _ from 'loadsh-es'

    lodash-es可以看到引入很多其他方的依赖

     在vite 的处理下,在浏览器中就可以看到 都转成了函数方法

     如果在vite.config.js 配置文件加

    export default {

        exclude:["loadsh-es"],//当遇到loadsh-es这个依赖的时候,不进行依赖预构建

    }

    这个时候看到的,就跟node_modules /lodash-es文件一样,引入很多的依赖包。

     所以vite 依赖预构建很好的解决引入第三方包有多依赖的情况。

    六 环境变量的处理

    (一)环境变量理解

    会根据当前的代码环境产生值的变化的变量就叫环境变量。

    代码环境:

    开发环境.env.development

    测试环境 .env.staging

    生产坏境 .env.production

    vite中使用环境变量:import.meta.env

    import.meta.env.Mode : 获取环境(如development 开发环境,production 生产环境)

    import.meta.env.BASE_URL:  部署用的基本url

    import.meta.env.PROD :PROP是production的缩写,表示应用是否允许在生产环境中

    import.meta.env.dev:表示应用是否允许在开发环境中

    七 模块热重载

    1. interface ImportMeta {
    2. readonly hot?: {
    3. //data 表示更新模块的不同实例之间持久化:也就是它可以用于将信息从模块的前一个版本传递到下一个版本
    4. readonly data: any
    5. //accept:表示在模块移除的时候执行的一些回调
    6. accept(): void
    7. accept(cb: (mod: any) => void): void
    8. accept(dep: string, cb: (mod: any) => void): void
    9. accept(deps: string[], cb: (mods: any[]) => void): void
    10. //prune:表示在模块移除的时候,执行这个回调
    11. prune(cb: () => void): void
    12. //dispose:可以接受自身的模块或者是一个期望被其他模块接受的模块,可以使用dispose来清除任何 尤其更新副本产生的持久的副作用
    13. dispose(cb: (data: any) => void): void
    14. //decline:表示模块不可热更新,如果在传播HMR更新时,遇到这个模块,浏览器应该执行完全的重新加载
    15. decline(): void
    16. //invalidate:这个方法现在执行的时候,只是重新加载页面
    17. invalidate(): void
    18. // on:表示可以监听自定义的HMR
    19. on(event: string, cb: (...args: any[]) => void): void
    20. }
    21. }

    八 vite 功能

    (一)vite+vue3 支持JSX和ts

    Vue 3 JSX 支持:@vitejs/plugin-vue-jsx

    操作如:输入命令: npm init vite vite-vue3 

    vite.config.ts用于存放vite的配置的地方。目前只有vue3官方提供的plugin。

    import { defineConfig } from 'vite'

    import vue from '@vitejs/plugin-vue'

    // https://vitejs.dev/config/

    export default defineConfig({

      plugins: [vue()]

    })

    这里plugin-vue支持app.vue,但不支持app.jsx、app.tsx.如果需要,需要安装vue3官方提供的一个插件plugin-vue-jsx。

    npm install -D @vitejs/plugin-vue-jsx

    修改vite.config.ts引入

    import { defineConfig } from 'vite'

    import vue from '@vitejs/plugin-vue'

    import vueJsx from '@vitejs/plugin-vue-jsx'

    // https://vitejs.dev/config/

    export default defineConfig({

        plugins: [vue(), vueJsx()]

    })

    删除src\App.vue,新建src\App.jsx (ts的话,就把App.ts)

    import { defineComponent } from 'vue'

    export default defineComponent({

        setup() {

            return () => {

                return

    Hello Vue3 jsx

            }

        }

    })

    把main.ts 改成main.jsx(ts:main.ts)

    import { createApp } from 'vue'

    import './style.css'

    import App from './App'

    createApp(App).mount('#app')

    运行可以看到

     (二)Vite中使用CSS

    • Vite支持原生最新的CSS——css variable
    • 集成了postcss。
    • @import alias
    • css-modules
    • css pre-processors

     (1)Vite支持原生最新的CSS——css variable

    style.css

    .color-red {

        color: red;

    }

    App.jsx 

    import { defineComponent } from 'vue'

    export default defineComponent({

        setup() {

            return () => {

                return

    Hello Vue3 jsx

            }

        }

    })

    css variables让我们可以在CSS中使用变量(变量名前一定要有两个-,否则不生效;然后使用var()方式来获取变量值),变量可以被覆盖,如果在低层级重新赋值则会覆盖样式。

    style.css 

    :root {

        --main-bg-color: #ccc;

    }

    .color-red {

        color:  var(--main-bg-color);

    }

      (2)集成了postcss

    Postcss是一个使用js插件来转换样式的工具,Postcss 的插件会检查你的css。
    postcss 一种对css编译的工具,类似babel对js的处理,

    (3) @import alias 别名

    为防止出现无数的路径指引长度。可以配置修改vite.config.ts如下:

    import { defineConfig } from 'vite'

    import vue from '@vitejs/plugin-vue'

    import vueJsx from '@vitejs/plugin-vue-jsx'

    // https://vitejs.dev/config/

    export default defineConfig({

        plugins: [vue(), vueJsx()],

        resolve: {

            alias: {

                '@': '/src',

                '@assets': '/src/assets',

            },

        },

    })

     assets/css/index.css

    .fontSize {

        font-size: 30px;

    }

    App.jsx

    import { defineComponent } from 'vue'

    import '@assets/css/index.css'

    export default defineComponent({

        setup() {

            return () => {

                return

    Hello Vue3 jsx

            }

        }

    })

    (4)CSS Modules

    任何以 .module.css 为后缀名的 CSS 文件都被认为是一个 CSS modules 文件

    css modules优势

    • 解决全局命名冲突问题 css modules只关心组件本身 命名唯一
    • 模块化 可以使用composes来引入自身模块中的样式以及另一个模块的样式
    • 解决嵌套层次过深的问题 使用扁平化的类名

    导入这样的文件会返回一个相应的模块对象:

     assets/css/example.module.css

    .fontWeight {

        font-weight: 300;

    }

    App.jsx 

    import { defineComponent } from 'vue'

    import '@assets/css/index.css'

    import classes from '@assets/css/example.module.css'

    export default defineComponent({

        setup() {

            return () => {

                return

    Hello Vue3 jsx

            }

        }

    })

    如果 css.modules.localsConvention 设置开启了 camelCase 格式变量名转换(例如 localsConvention: 'camelCaseOnly'),你还可以使用按名导入。 

    修改vite.config.ts

    import { defineConfig } from 'vite'

    import vue from '@vitejs/plugin-vue'

    import vueJsx from '@vitejs/plugin-vue-jsx'

    // https://vitejs.dev/config/

    export default defineConfig({

       ...

        css:{

            modules:{

                // 小驼峰

                localsConvention:'camelCase'

            }

        }

    })

    App.jsx 

    import { defineComponent } from 'vue'

    import '@assets/css/index.css'

    import { fontWeight } from '@assets/css/example.module.css'

    export default defineComponent({

        setup() {

            return () => {

                return

    Hello Vue3 jsx

            }

        }

    })

    (5)CSS 预处理器

    Vite 也同时提供了对 .scss.sass.less.styl 和 .stylus 文件的内置支持。没有必要为它们安装特定的 Vite 插件,但必须安装相应的预处理器依赖:

    npm install -D sass  npm install -D less


     (三)vite中使用Web Worker

            javaScript语言采用的事单线程编程,也就是说所有的任务只能在一个线程上完成,一次只能做一件事,那前面的任务没做完,后面的任务只能等着。那随着完美电脑计算能力的增强,尤其是多核的cpu的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。

            那 web worker 的作用就是JavaScript创造的多线程环境,它是在html5出现的时候随机推出来的,它允许主线程来创建 worker 线程,将一些任务分配给后者来运行。在主线程运行的同时,worker线程在后台运行 ,俩者互不干扰,等到worker线程完成计算任务 ,再把结果返回给主线程。

    这样做的好处就是一些计算密集型或者是高延迟的任务被worker线程负担了,而主线程就会很流畅。比如说主线程通常会负责ui的交互,不会阻塞或者是拖慢,那worker线程一旦创建成功就会始终运行,不会被主线程的活动所打断(比如像用户点击按钮啊,提交表单啊等等)这样就有利于随时响应我们主线程的通行。

    纯js下使用 web worker线程

    const worker = new  Worker('./worker.js')

    vite中使用 web worker线程

    import MyWorker from './worker?worker'

    const worker = new MyWorker()

    如下案例:

    worker.js

    1. var i = 0;
    2. function timeCount() {
    3. i++;
    4. postMessage(i);
    5. setTimeout(timeCount, 1000);
    6. }
    7. timeCount();

     main.js

    1. //纯js
    2. const worker = new Worker('./worker.js')
    3. worker.onmessage = function (ev) {
    4. console.log('worker', ev.data)
    5. }
    6. // vite中使用 web worker线程
    7. import MyWorker from './worker?worker'
    8. const worker = new MyWorker()
    9. worker.onmessage = function (ev) {
    10. console.log('worker', ev.data)
    11. }

     (四)vite中使用WebAssembly

    WebAssembly是一个可移植,体积小,加载快并且兼容Web的全新技术,是大前端的重要内容。他可以实现JS在宿主环境中(比如在浏览器里边,直接的调用c c++等程序),前端的性能从此有了更好的解决方案。大概流程如下

    1.  编译一个.wasm 环境 

    2. vite 中调用.wasm

    import init from './example.wasm'

    init().then((exports) => {
      exports.test()
    })

     (五)vite中导入json及GLOB

    json文件直接导入

    1. // 导入整个对象
    2. import json from './example.json'
    3. // 对一个根字段使用具名导入 —— 有效帮助 treeshaking!
    4. import { field } from './example.json'

     Glob 导入

    Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块:

    在glob文件下创建json/js等文件

    const modules = import.meta.glob('./glob/*');

    console.log(module)

    //打印出来内容如

     modules = {
      './glob/foo.json: () => import('./glob/foo.json'),
      './dir/bar.js': () => import('./glob/bar.js')
    }

    九 浏览器兼容性

    支持原生ESM script标签、支持原生ESM 动态导入。

    import { defineConfig } from 'vite'

    import vue from '@vitejs/plugin-vue'

    // https://vitejs.dev/config/

    export default defineConfig({

        plugins: [vue()],

        build:{

         target:'es2015'// 这样的话,打包的文件就可以支持es6文件

        }

    })

    低版本的浏览器兼容,需要安装插件

    npm i @vitejs/plugin-legacy -D

    vite.config.ts 文件填写配置 

    import { defineConfig } from 'vite'

    import vue from '@vitejs/plugin-vue'

    import legacyPlugin from '@vitejs/plugin-legacy'

    // https://vitejs.dev/config/

    export default defineConfig({

        plugins: [

            vue(),

            legacyPlugin({

                targets:['chrome 52'],  // 需要兼容的目标列表,可以设置多个

                additionalLegacyPolyfills:['regenerator-runtime/runtime'] // 面向IE11时需要此插件

            })

        ]

    })

    打包、运行到浏览器。

    新版本浏览器运行正常!旧版本浏览器运行正常!

    注意:@vitejs/plugin-legacy不能使vue3支持ie11

    十 服务端渲染SSR-vue3

    创建项目vite-ssr-vue3 

    操作如:输入命令: npm init vite vite-ssr-vue3 

    编写一个简单的vue3项目

    页面创建:src/pages 文件下创建 home.vue 和about.vue

    如:home.vue

    1. <template>
    2. <div>Home</div>
    3. </template>
    4. <script>
    5. export default {
    6. }
    7. </script>
    8. <style>
    9. </style>

    App.vue

    1. <template>
    2. <div>
    3. <router-link to="/">Home</router-link>
    4. <router-link to="/about">About</router-link>
    5. <router-view></router-view>
    6. </div>
    7. </template>
    8. <script>
    9. export default {
    10. }
    11. </script>
    12. <style>
    13. </style>

    路由创建:src/router/index,js

    1. import { createRouter as _createRouter, createWebHistory } from 'vue-router'
    2. // 载入pages里面定义的所有的组件
    3. const pages = import.meta.glob('../pages/*.vue');
    4. // 获取路由
    5. const routes = Object.keys(pages).map(path => {
    6. const name = path.match(/\.\.\/pages(.*)\.vue$/)[1].toLowerCase();
    7. return {
    8. path: name === '/home' ? '/' : name,
    9. component: pages[path],//()=>import('../pages/*.vue')
    10. }
    11. })
    12. export function createRouter() {
    13. return _createRouter({
    14. history: createWebHistory(),
    15. routes
    16. })
    17. }

    引入路由:main.js

    1. import { createApp } from 'vue'
    2. import './style.css'
    3. import App from './App.vue'
    4. import { createRouter } from './router/index.js'
    5. createApp(App).use(createRouter()).mount('#app')

    运行

     

    转成服务端渲染ssr--开发环境

    创建node服务:

    安装 npm i express -D

    创建vite-ssr-vue3/server.js 

    1. const fs = require('fs')
    2. const path = require('path')
    3. const express = require('express')
    4. const { createServer: createViteServer } = require('vite')
    5. async function createServer() {
    6. const app = express()
    7. // 以中间件模式创建 Vite 应用,这将禁用 Vite 自身的 HTML 服务逻辑
    8. // 并让上级服务器接管控制
    9. //
    10. // 如果你想使用 Vite 自己的 HTML 服务逻辑(将 Vite 作为
    11. // 一个开发中间件来使用),那么这里请用 'html'
    12. const vite = await createViteServer({
    13. server: { middlewareMode: 'ssr' }
    14. })
    15. // 使用 vite 的 Connect 实例作为中间件
    16. app.use(vite.middlewares)
    17. app.use('*', async (req, res) => {
    18. //* 处理程序供给服务端渲染的 HTM
    19. const url = req.originalUrl
    20. try {
    21. // 1. 读取 index.html
    22. let template = fs.readFileSync(
    23. path.resolve(__dirname, 'index.html'),
    24. 'utf-8'
    25. )
    26. // 2. 应用 Vite HTML 转换。这将会注入 Vite HMR 客户端,
    27. // 同时也会从 Vite 插件应用 HTML 转换。
    28. // 例如:@vitejs/plugin-react-refresh 中的 global preambles
    29. template = await vite.transformIndexHtml(url, template)
    30. // 3. 加载服务器入口。vite.ssrLoadModule 将自动转换
    31. // 你的 ESM 源码使之可以在 Node.js 中运行!无需打包
    32. // 并提供类似 HMR 的根据情况随时失效。
    33. const { render } = await vite.ssrLoadModule('/src/entry-server.js')
    34. // 4. 渲染应用的 HTML。这假设 entry-server.js 导出的 `render`
    35. // 函数调用了适当的 SSR 框架 API。
    36. // 例如 ReactDOMServer.renderToString()
    37. const appHtml = await render(url)
    38. // 5. 注入渲染后的应用程序 HTML 到模板中。
    39. const html = template.replace(`<!--ssr-outlet-->`, appHtml)
    40. // 6. 返回渲染后的 HTML。
    41. res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
    42. } catch (e) {
    43. // 如果捕获到了一个错误,让 Vite 来修复该堆栈,这样它就可以映射回
    44. // 你的实际源码中。
    45. vite.ssrFixStacktrace(e)
    46. console.error(e)
    47. res.status(500).end(e.message)
    48. }
    49. })
    50. app.listen(3000)
    51. }
    52. createServer()

    模块化的转化:src/entry-server.js   

    1. import {createApp} from './main'
    2. import { renderToString} from 'vue/server-renderer'
    3. export async function render(url){
    4. const {app,router} = createApp();
    5. router.push(url);
    6. await render.isReady();
    7. const ctx = {};
    8. const html = await renderToString(app,ctx);
    9. return html;
    10. }

    main.js

    1. import { createSSRApp} from 'vue'
    2. import './style.css'
    3. import App from './App.vue'
    4. import { createRouter } from './router/index.js'
    5. export function createApp(){
    6. const app = createSSRApp(App);
    7. const router = createRouter();
    8. app.use(router);
    9. return {
    10. app,
    11. router
    12. }
    13. }

     

    在html增加 为了,替换内容

    改造路由::src/router/index,js

    1. import { createRouter as _createRouter, createWebHistory, createMemoryHistory } from 'vue-router'
    2. // 载入pages里面定义的所有的组件
    3. const pages = import.meta.glob('../pages/*.vue');
    4. // 获取路由
    5. const routes = Object.keys(pages).map(path => {
    6. const name = path.match(/\.\.\/pages(.*)\.vue$/)[1].toLowerCase();
    7. return {
    8. path: name === '/home' ? '/' : name,
    9. component: pages[path],//()=>import('../pages/*.vue')
    10. }
    11. })
    12. export function createRouter() {
    13. return _createRouter({
    14. history: import.meta.env.SSR ? createMemoryHistory() : createWebHistory(),
    15. routes
    16. })
    17. }

     运行:

     html 的内容都显示出来,这样做搜索引擎的话容易了。

    将来我们爬虫,爬取我们的页面的时候是可以爬取到相关的内容。服务端渲染的一个最大的意义所在。

    十一 参考

    Vite学习指南

    Vite世界指南(带你从0到1深入学习 vite)

    封装dotenv库实现类似Vite加载环境变量的行为

  • 相关阅读:
    Ubuntu20.04下搭建Hadoop伪分布式集群
    【Zabbix监控二】之zabbix自定义监控内容案例(自动发现、自动注册)
    【Java】有 A、B、C 三个线程,如何保证三个线程同时执行?在并发情况下,如何保证三个线程依次执行?如何保证三个线程有序交错执行?
    体验不尽,进化不止,看视频云技术六大创新
    ES6之对象解构
    易周金融分析 | 银行理财市场渐趋理性;“睡眠信用卡”持续清退
    LNMP编译安装
    安装Hive集群
    springboot/ssm航班进出港管理系统Java航班信息记录管理系统web
    基于SSM的教学管理系统设计与实现
  • 原文地址:https://blog.csdn.net/gao_xu_520/article/details/126350004