主頁 > 企業開發 > 筆記:vite 的啟動鏈路以及背后的部分原理

筆記:vite 的啟動鏈路以及背后的部分原理

2022-02-17 06:45:57 企業開發

這里分享一篇找到的文章

原文地址:https://blog.csdn.net/frontend_frank/article/details/106632197

NO.1

vite 是什么

vite —— 一個由 vue 作者尤雨溪開發的 web 開發工具,它具有以下特點:

  1. 快速的冷啟動

  2. 即時的模塊熱更新

  3. 真正的按需編譯

從作者在微博上的發言:Vite,一個基于瀏覽器原生 ES imports 的開發服務器,利用瀏覽器去決議 imports,在服務器端按需編譯回傳,完全跳過了打包這個概念,

服務器隨起隨用,同時不僅有 Vue 檔案支持,還搞定了熱更新,而且熱更新的速度不會隨著模塊增多而變慢,
針對生產環境則可以把同一份代碼用 rollup 打,雖然現在還比較粗糙,但這個方向我覺得是有潛力的,做
得好可以徹底解決改一行代碼等半天熱更新的問題,

中可以看出 vite 主要特點是基于瀏覽器 native 的 ES module (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) 來開發,省略打包這個步驟,因為需要什么資源直接在瀏覽器里引入即可,

基于瀏覽器 ES module 來開發 web 應用也不是什么新鮮事,snowpack 也基于此,不過目前此專案社區中并沒有流行起來,vite 的出現也許會讓這種開發方式再火一陣子,

有趣的是 vite 算是革了 webpack 的命了(生產環境用 rollup),所以 webpack 的開發者直接喊大哥了...

NO.2

vite 的使用方式

同常見的開發工具一樣,vite 提供了用 npm 或者 yarn 一建生成專案結構的方式,使用 yarn 在終端執行:

$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev

即可初始化一個 vite 專案(默認應用模板為 vue3.x),生成的專案結構十分簡潔:

|____node_modules
|____App.vue // 應用入口
|____index.html // 頁面入口
|____vite.config.js // 組態檔
|____package.json

執行 yarn dev 即可啟動應用 ,

NO.3

vite 啟動鏈路

命令決議

這部分代碼在 src/node/cli.ts 里,主要內容是借助 minimist —— 一個輕量級的命令決議工具決議 npm scripts,決議的函式是 resolveOptions ,精簡后的代碼片段如下,

function resolveOptions() {
    // command 可以是 dev/build/optimize
    if (argv._[0]) {
        argv.command = argv._[0];
    }
    return argv;
}

拿到 options 后,會根據 options.command 的值判斷是執行在開發環境需要的 runServe 命令或生產環境需要的 runBuild 命令,

if (!options.command || options.command === 'serve') {
    runServe(options)
 } else if (options.command === 'build') {
    runBuild(options)
 } else if (options.command === 'optimize') {
    runOptimize(options)
 }

在 runServe 方法中,執行 server 模塊的創建開發服務器方法,同樣在 runBuild 中執行 build 模塊的構建方法,

最新的版本中還增加了 optimize 命令的支持,關于 optimize 做了什么,我們下文再說,

server

這部分代碼在 src/node/server/index.ts 里,主要暴露一個 createServer 方法,

vite 使用 koa 作 web server,使用 clmloader 創建了一個監聽檔案改動的 watcher,同時實作了一個插件機制,將 koa-app 和 watcher 以及其他必要工具組合成一個 context 物件注入到每個 plugin 中,

context 組成如下:

plugin 依次從 context 里獲取上面這些組成部分,有的 plugin 在 koa 實體添加了幾個 middleware,有的借助 watcher 實作對檔案的改動監聽,這種插件機制帶來的好處是整個應用結構清晰,同時每個插件處理不同的事情,職責更分明,

plugin

上文我們說到 plugin,那么有哪些 plugin 呢?它們分別是:

  • 用戶注入的 plugins —— 自定義 plugin

  • hmrPlugin —— 處理 hmr

  • htmlRewritePlugin —— 重寫 html 內的 script 內容

  • moduleRewritePlugin —— 重寫模塊中的 import 匯入

  • moduleResolvePlugin ——獲取模塊內容

  • vuePlugin —— 處理 vue 單檔案組件

  • esbuildPlugin —— 使用 esbuild 處理資源

  • assetPathPlugin —— 處理靜態資源

  • serveStaticPlugin —— 托管靜態資源

  • cssPlugin —— 處理 css/less/sass 等參考

我們來看 plugin 的實作方式,開發一個用來攔截 json 檔案 plugin 可以這么實作:

interface ServerPluginContext {
  root: string
  app: Koa
  server: Server
  watcher: HMRWatcher
  resolver: InternalResolver
  config: ServerConfig
}
 
type ServerPlugin = (ctx:ServerPluginContext)=> void;
 
const JsonInterceptPlugin:ServerPlugin = ({app})=>{
    app.use(async (ctx, next) => {
      await next()
      if (ctx.path.endsWith('.json') && ctx.body) {
        ctx.type = 'js'
        ctx.body = `export default json`
      }
  })
}

vite 背后的原理都在 plugin 里,這里不再一一解釋每個 plugin 的作用,會放在下文背后的原理中一并討論,

build

這部分代碼在 node/build/index.ts 中,build 目錄的結構雖然與 server 相似,同樣匯出一個 build 方法,同樣也有許多 plugin,不過這些 plugin 與 server 中的用途不一樣,因為 build 使用了 rollup ,所以這些 plugin 也是為 rollup 打包的 plugin ,本文就不再多提,

NO.4

vite 運行原理

ES module

要了解 vite 的運行原理,首先要知道什么是 ES module,目前流覽器對其的支持如下:

 主流的瀏覽器(IE11除外)均已經支持,其最大的特點是在瀏覽器端使用 export import 的方式匯入和匯出模塊,在 script 標簽里設定 type="module" ,然后使用模塊內容,

<script type="module">
  import { bar } from './bar.js‘
</script>

當 html 里嵌入上面的 script 標簽時候,瀏覽器會發起 http 請求,請求 htttp server 托管的 bar.js ,在 bar.js 里,我們用 named export 匯出 bar 變數,在上面的 script 中能獲取到 bar 的定義,

// bar.js 
export const bar = 'bar';

在 vite 中的作用

打開運行中的 vite 專案,訪問 view-source 可以發現 html 里有段這樣的代碼:

<script type="module">
    import { createApp } from '/@modules/vue'
    import App from '/App.vue'
    createApp(App).mount('#app')
</script>

從這段代碼中,我們能 get 到以下幾點資訊:

從 http://localhost:3000/@modules/vue 中獲取 createApp 這個方法

從 http://localhost:3000/App.vue 中獲取應用入口

使用 createApp 創建應用并掛載節點

createApp 是 vue3.X 的 api,只需知道這是創建了 vue 應用即可,vite 利用 ES module,把 “構建 vue 應用” 這個本來需要通過 webpack 打包后才能執行的代碼直接放在瀏覽器里執行,這么做是為了:

去掉打包步驟

實作按需加載

去掉打包步驟

打包的概念是開發者利用打包工具將應用各個模塊集合在一起形成 bundle,以一定規則讀取模塊的代碼——以便在不支持模塊化的瀏覽器里使用,

為了在瀏覽器里加載各模塊,打包工具會借助膠水代碼用來組裝各模塊,比如 webpack 使用 map存放模塊 id 和路徑,使用 __webpack_require__  方法獲取模塊匯出,

vite 利用瀏覽器原生支持模塊化匯入這一特性,省略了對模塊的組裝,也就不需要生成 bundle,所以打包這一步就可以省略了,

實作按需打包

前面說到,webpack 之類的打包工具會將各模塊提前打包進 bundle 里,但打包的程序是靜態的——不管某個模塊的代碼是否執行到,這個模塊都要打包到 bundle 里,這樣的壞處就是隨著專案越來越大打包后的 bundle 也越來越大,

開發者為了減少 bundle 大小,會使用動態引入 import() 的方式異步的加載模塊( 被引入模塊依然需要提前打包),又或者使用 tree shaking 等方式盡力的去掉未參考的模塊,然而這些方式都不如 vite 的優雅,vite 可以只在需要某個模塊的時候動態(借助 import() )的引入它,而不需要提前打包,雖然只能用在開發環境,不過這就夠了,

vite 如何處理 ESM

既然 vite 使用 ESM 在瀏覽器里使用模塊,那么這一步究竟是怎么做的?

上文提到過,在瀏覽器里使用 ES module 是使用 http 請求拿到模塊,所以 vite 必須提供一個 web server 去代理這些模塊,上文中提到的 koa 就是負責這個事情,vite 通過對請求路徑的劫持獲取資源的內容回傳給瀏覽器,不過 vite 對于模塊匯入做了特殊處理,

@modules 是什么?

通過工程下的 index.html 和開發環境下的 html 源檔案對比,發現 script 標簽里的內容發生了改變,由

<script type="module">
    import { createApp } from 'vue'
    import App from '/App.vue'
    createApp(App).mount('#app')
</script>

變成了

<script type="module">
    import { createApp } from '/@modules/vue'
    import App from '/App.vue'
    createApp(App).mount('#app')
</script>

vite 對 import 都做了一層處理,其程序如下:

在 koa 中間件里獲取請求 body

通過 es-module-lexer 決議資源 ast 拿到 import 的內容

判斷 import 的資源是否是絕對路徑,絕對視為 npm 模塊

回傳處理后的資源路徑:"vue" => "/@modules/vue"

這部分代碼在 serverPluginModuleRewrite 這個 plugin 中,

為什么需要 @modules?

如果我們在模塊里寫下以下代碼的時候,瀏覽器中的 esm 是不可能獲取到匯入的模塊內容的:

import vue from 'vue'

因為 vue 這個模塊安裝在 node_modules 里,以往使用 webpack,webpack遇到上面的代碼,會幫我們做以下幾件事:

  • 獲取這段代碼的內容

  • 決議成 AST

  • 遍歷 AST 拿到 import 陳述句中的包的名稱

  • 使用 enhanced-resolve 拿到包的實際地址進行打包,

但是瀏覽器中 ESM 無法直接訪問專案下的 node_modules,所以 vite 對所有 import 都做了處理,用帶有 @modules 的前綴重寫它們,

從另外一個角度來看這是非常比較巧妙的做法,把檔案路徑的 rewrite 都寫在同一個 plugin 里,這樣后續如果加入更多邏輯,改動起來不會影響其他 plugin,其他 plugin 拿到資源路徑都是 @modules ,比如說后續可能加入 alias 的配置:就像 webpack alias 一樣:可以將專案里的本地檔案配置成絕對路徑的參考,

怎么回傳模塊內容

在下一個 koa middleware 中,用正則匹配到路徑上帶有 @modules 的資源,再通過 require('xxx') 拿到 包的匯出回傳給瀏覽器,

以往使用 webpack 之類的打包工具,它們除了將模塊組裝到一起形成 bundle,還可以讓使用了不同模塊規范的包互相參考,比如:

ES module (esm) 匯入 cjs

CommonJS (cjs) 匯入 esm

dynamic import 匯入 esm

dynamic import 匯入 cjs

關于 es module 的坑可以看這篇文章(https://zhuanlan.zhihu.com/p/40733281),

起初在 vite 還只是為 vue3.x 設計的時候,對 vue esm 包是經過特殊處理的,比如:需要 @vue/runtime-dom 這個包的內容,不能直接通過 require('@vue/runtime-dom')得到,而需要通過 require('@vue/runtime-dom/dist/runtime-dom.esm-bundler.js' 的方式,這樣可以使得 vite 拿到符合 esm 模塊標準的 vue 包,

目前社區中大部分模塊都沒有設定默認匯出 esm,而是匯出了 cjs 的包,既然 vue3.0 需要額外處理才能拿到 esm 的包內容,那么其他日常使用的 npm 包是不是也同樣需要支持?答案是肯定的,目前在 vite 專案里直接使用 lodash 還是會報錯的,

不過 vite 在最近的更新中,加入了 optimize 命令,這個命令專門為解決模塊參考的坑而開發,例如我們要在 vite 中使用 lodash,只需要在 vite.config.js (vite 組態檔)中,配置 optimizeDeps 物件,在 include 陣列中添加 lodash,

// vite.config.js
module.exports = {
  optimizeDeps: {
    include: ["lodash"]
  }
}
這樣 vite 在執行 runOptimize 的時候中會使用 roolup 對 lodash 包重新編譯,將編譯成符合 esm 模塊規范的新的包放入 node_modules 下的 .vite_opt_cache 中,然后配合 resolver 對 lodash 的匯入進行處理:使用編譯后的包內容代替原來 lodash 的包的內容,這樣就解決了 vite 中不能使用 cjs 包的問題,這部分代碼在  depOptimizer.ts 里,

不過這里還有個問題,由于在 depOptimizer.ts 中,vite 只會處理在專案下 package.json 里的 dependencies 里宣告好的包進行處理,所以無法在專案里使用

import pick from 'lodash/pick'

的方式單使用 pick 方法,而要使用

import lodash from 'lodash'
 
lodash.pick()

的方式,這可能在生產環境下使用某些包的時候對 bundle 的體積有影響,

回傳模塊的內容的代碼在:serverPluginModuleResolve.ts 這個 plugin 中,

vite 如何編譯模塊

最初 vite 為 vue3.x 開發,所以這里的編譯指的是編譯 vue 單檔案組件,其他 es 模塊可以直接匯入內容,

SFC

vue 單檔案組件(簡稱 SFC) 是 vue 的一個亮點,前端屆對 SFC 褒貶不一,個人看來,SFC 是利大于弊的,雖然 SFC 帶來了額外的開發作業量,比如為了解析 template 要寫模板決議器,還要在 SFC 中決議出邏輯和樣式,在 vscode 里要寫 vscode 插件,在 webpack 里要寫 vue-loader,但是對于使用方來說可以在一個檔案里可以同時寫 template、js、style,省了各檔案互相跳轉,

與 vue-loader 相似,vite 在決議 vue 檔案的時候也要分別處理多次,我們打開瀏覽器的 network,可以看到:

1 個請求的 query 中什么都沒有,另 2 個請求分別通過在 query 里指定了 type 為 style 和 template,

先來看看如何將一個 SFC 變成多個請求,我們從第一次請求開始分析,簡化后的代碼如下:

function vuePlugin({app}){
  app.use(async (ctx, next) => {
    if (!ctx.path.endsWith('.vue') && !ctx.vue) {
      return next()
    }
 
    const query = ctx.query
    // 獲取檔案名稱
    let filename = resolver.requestToFile(publicPath)
 
    // 決議器決議 SFC
    const descriptor = await parseSFC(root, filename, ctx.body)
    if (!descriptor) {
      ctx.status = 404
      return
    }
    // 第一次請求 .vue
    if (!query.type) {
      if (descriptor.script && descriptor.script.src) {
        filename = await resolveSrcImport(descriptor.script, ctx, resolver)
      }
      ctx.type = 'js'
      // body 回傳決議后的代碼
      ctx.body = await compileSFCMain(descriptor, filename, publicPath)
    }
    
    // ...
}

在 compileSFCMain 中是一段長長的 generate 代碼:

function compileSFCMain(descriptor, filePath: string, publicPath: string) {
  let code = ''
  if (descriptor.script) {
    let content = descriptor.script.content
    code += content.replace(`export default`, 'const __script =')
  } else {
    code += `const __script = {}`
  }
 
  if (descriptor.styles) {
    code += `\nimport { updateStyle } from "${hmrClientId}"\n`
    descriptor.styles.forEach((s, i) => {
      const styleRequest = publicPath + `?type=style&index=${i}`
      code += `\nupdateStyle("${id}-${i}", ${JSON.stringify(styleRequest)})`
    })
    if (hasScoped) {
      code += `\n__script.__scopeId = "data-v-${id}"`
    }
  }
 
  if (descriptor.template) {
    code += `\nimport { render as __render } from ${JSON.stringify(
      publicPath + `?type=template`
    )}`
    code += `\n__script.render = __render`
  }
  code += `\n__script.__hmrId = ${JSON.stringify(publicPath)}`
  code += `\n__script.__file = ${JSON.stringify(filePath)}`
  code += `\nexport default __script`
  return code
}

直接看 generate 后的代碼:

import { updateStyle } from "/vite/hmr"
updateStyle("c44b8200-0", "/App.vue?type=style&index=0")
__script.__scopeId = "data-v-c44b8200"
import { render as __render } from "/App.vue?type=template"
__script.render = __render
__script.__hmrId = "/App.vue"
__script.__file = "/Users/muou/work/playground/vite-app/App.vue"
export default __script

出現了 vite/hmr 的匯入,vite/hmr 具體內容我們下文再分析,從這段代碼中可以看到,對于 style vite 使用 updateStyle 這個方法處理,updateStyle 內容非常簡單,這里就不貼代碼了,就做了 1 件事:通過創建 style 元素,設定了它的 innerHtml 為 css 內容,

這兩種方式都會使得瀏覽器發起 http 請求,這樣就能被 koa 中間件捕獲到了,所以就形成了上文我們看到的:對一個 .vue 檔案處理三次的情景,

這部分代碼在:serverPluginVue 這個 plugin 里,

css

如果在 vite 專案里引入一個 sass 檔案會怎么樣?

最初 vite 只是為 vue 專案開發,所以并沒有對 css 預編譯的支持,不過隨著后續的幾次大更新,在 vite 專案里使用 sass/less 等也可以跟使用 webpack 的時候一樣優雅了,只需要安裝對應的 css 前處理器即可,

在 cssPlugin 中,通過正則:/(.+).(less|sass|scss|styl|stylus)$/ 判斷路徑是否需要 css 預編譯,如果命中正則,就借助 cssUtils 里的方法借助 postcss 對要匯入的 css 檔案編譯,

vite 熱更新的實作

上文中出現了 vite/hmr ,這就是 vite 處理熱更新的關鍵,在 serverPluginHmr plugin 中,對于 path 等于  vite/hmr 做了一次判斷:

 app.use(async (ctx, next) => {
  if (ctx.path === '/vite/hmr') {
      ctx.type = 'js'
      ctx.status = 200
      ctx.body = hmrClient
  }
 }

hmrClient 是 vite 熱更新的客戶端代碼,需要在瀏覽器里執行,這里先來說說通用的熱更新實作,熱更新一般需要四個部分:

  1. 首先需要 web 框架支持模塊的 rerender/reload

  2. 通過 watcher 監聽檔案改動

  3. 通過 server 端編譯資源,并推送新模塊內容給 client ,

  1. client 收到新的模塊內容,執行 rerender/reload

vite 也不例外同樣有這四個部分,其中客戶端代碼在:client.ts 里,服務端代碼在 serverPluginHmr 里,對于 vue 組件的更新,通過 vue3.x 中的 HMRRuntime 處理的,

client 端

在 client 端, WebSocket 監聽了一些更新的型別,然后分別處理,它們是:

  • vue-reload —— vue 組件更新:通過 import 匯入新的 vue 組件,然后執行 HMRRuntime.reload

  • vue-rerender —— vue template 更新:通過 import 匯入新的 template ,然后執行 HMRRuntime.rerender

  • vue-style-update —— vue style 更新:直接插入新的 stylesheet

  • style-update —— css 更新:document 插入新的 stylesheet

  • style-remove —— css 移除:document 洗掉 stylesheet

  • js-update  —— js 更新:直接執行

  • full-reload —— 頁面 roload:使用 window.reload 重繪頁面

server 端

在 server 端,通過 watcher 監聽頁面改動,根據檔案型別判斷是 js Reload 還是 Vue Reload:

 watcher.on('change', async (file) => {
    const timestamp = Date.now()
    if (file.endsWith('.vue')) {
      handleVueReload(file, timestamp)
    } else if (
      file.endsWith('.module.css') ||
      !(file.endsWith('.css') || cssTransforms.some((t) => t.test(file, {})))
    ) {
      // everything except plain .css are considered HMR dependencies.
      // plain css has its own HMR logic in ./serverPluginCss.ts.
      handleJSReload(file, timestamp)
    }
  })

在 handleVueReload 方法里,會使用決議器拿到當前檔案的 template/script/style ,并且與快取里的上一次決議的結果進行比較,如果 template 發生改變就執行 vue-rerender,如果 style 發生改變就執行 vue-style-update,簡化后的邏輯如下:

async function handleVueReload(
    file
    timestamp,
    content
  ) {
    // 獲取快取
    const cacheEntry = vueCache.get(file)
 
    // 決議 vue 檔案                                 
    const descriptor = await parseSFC(root, file, content)
    if (!descriptor) {
      // read failed
      return
    }
    // 拿到上一次決議結果
    const prevDescriptor = cacheEntry && cacheEntry.descriptor
    
    // 設定重繪變數
    let needReload = false // script 改變標記
    let needCssModuleReload = false // css 改變標記
    let needRerender = false // template 改變標記
 
    // 判斷 script 是否相同
    if (!isEqual(descriptor.script, prevDescriptor.script)) {
      needReload = true
    }
 
     // 判斷 template 是否相同
    if (!isEqual(descriptor.template, prevDescriptor.template)) {
      needRerender = true
    }
      
    // 通過 send 發送 socket
    if (needRerender){
      send({
        type: 'vue-rerender',
        path: publicPath,
        timestamp
      })  
    }
  }

handleJSReload 方法則是根據檔案路徑參考,判斷被哪個 vue 組件所依賴,如果未找到 vue 組件依賴,則判斷頁面需要重繪,否則走組件更新邏輯,這里就不貼代碼了,

整體代碼在 client.ts 和 serverPluginHmr.ts 里,

如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/424971.html

標籤:其他

上一篇:vue3的組合式API究竟是什么

下一篇:大檔案切片上傳到服務器

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more