中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

webpack原理的示例分析

發布時間:2020-12-05 11:05:37 來源:億速云 閱讀:149 作者:小新 欄目:web開發

這篇文章給大家分享的是有關webpack原理的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。


0 配置文件

首先簡單看一下webpack配置文件(webpack.config.js):

var path = require('path');
var node_modules = path.resolve(__dirname, 'node_modules');
var pathToReact = path.resolve(node_modules, 'react/dist/react.min.js');

module.exports = {
  // 入口文件,是模塊構建的起點,同時每一個入口文件對應最后生成的一個 chunk。
  entry: {
    bundle: [
      'webpack/hot/dev-server',
      'webpack-dev-server/client?http://localhost:8080',
      path.resolve(__dirname, 'app/app.js')
    ]
  },
  // 文件路徑指向(可加快打包過程)。
  resolve: {
    alias: {
      'react': pathToReact
    }
  },
  // 生成文件,是模塊構建的終點,包括輸出文件與輸出路徑。
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].js'
  },
  // 這里配置了處理各模塊的 loader ,包括 css 預處理 loader ,es6 編譯 loader,圖片處理 loader。
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel',
        query: {
          presets: ['es2015', 'react']
        }
      }
    ],
    noParse: [pathToReact]
  },
  // webpack 各插件對象,在 webpack 的事件流中執行對應的方法。
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
};

1. 工作原理概述

1.1 基本概念

在了解webpack原理之前,需要掌握以下幾個核心概念

  • Entry: 入口,webpack構建第一步從entry開始

  • module:模塊,在webpack中一個模塊對應一個文件。webpack會從entry開始,遞歸找出所有依賴的模塊

  • Chunk:代碼塊,一個chunk由多個模塊組合而成,用于代碼合并與分割

  • Loader: 模塊轉換器,用于將模塊的原內容按照需求轉換成新內容

  • Plugin:拓展插件,在webpack構建流程中的特定時機會廣播對應的事件,插件可以監聽這些事件的發生,在特定的時機做對應的事情

1.2 流程概述

webpack從啟動到結束依次執行以下操作:

graph TD
初始化參數 --> 開始編譯 
開始編譯 -->確定入口 
確定入口 --> 編譯模塊
編譯模塊 --> 完成編譯模塊
完成編譯模塊 --> 輸出資源
輸出資源 --> 輸出完成

各個階段執行的操作如下:

  1. 初始化參數:從配置文件(默認webpack.config.js)和shell語句中讀取與合并參數,得出最終的參數

  2. 開始編譯(compile):用上一步得到的參數初始化Comiler對象,加載所有配置的插件,通過執行對象的run方法開始執行編譯

  3. 確定入口:根據配置中的entry找出所有的入口文件

  4. 編譯模塊:從入口文件出發,調用所有配置的Loader對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到所有入口依賴的文件都經過處理

  5. 完成編譯模塊:經過第四步之后,得到了每個模塊被翻譯之后的最終內容以及他們之間的依賴關系

  6. 輸出資源:根據入口和模塊之間的依賴關系,組裝成一個個包含多個模塊的chunk,再將每個chunk轉換成一個單獨的文件加入輸出列表中,這是可以修改輸出內容的最后機會

  7. 輸出完成:在確定好輸出內容后,根據配置(webpack.config.js && shell)確定輸出的路徑和文件名,將文件的內容寫入文件系統中(fs)

在以上過程中,webpack會在特定的時間點廣播特定的事件,插件監聽事件并執行相應的邏輯,并且插件可以調用webpack提供的api改變webpack的運行結果

1.3 流程細節

webpack構建流程可分為以下三大階段。

  1. 初始化:啟動構建,讀取與合并配置參數,加載plugin,實例化Compiler

  2. 編譯:從Entry出發,針對每個Module串行調用對應的Loader去翻譯文件中的內容,再找到該Module依賴的Module,遞歸的進行編譯處理

  3. 輸出:將編譯后的Module組合成Chunk,將Chunk轉換成文件,輸出到文件系統中

如果只執行一次,流程如上,但在開啟監聽模式下,流程如下圖

graph TD

  初始化-->編譯;
  編譯-->輸出;
  輸出-->文本發生變化
  文本發生變化-->編譯
1.3.1初始化階段

在初始化階段會發生的事件如下

事件描述
初始化參數從配置文件和shell語句中讀取與合并參數,得出最終的參數,這個過程還會執行配置文件中的插件實例化語句 new Plugin()
實例化Compiler實例化Compiler,傳入上一步得到的參數,Compiler負責文件監聽和啟動編譯。在Compiler實例中包含了完整的webpack配置,全局只有一個Compiler實例。
加載插件依次調用插件的apply方法,讓插件可以監聽后續的所有事件節點。同時向插件中傳入compiler實例的引用,以方便插件通過compiler調用webpack的api
environment開始應用Node.js風格的文件系統到compiler對象,以方便后續的文件尋找和讀取
Entry-option讀取配置的Entrys,為每個Entry實例化一個對應的EntryPlugin,為后面該Entry的遞歸解析工作做準備
After-plugins調用完所有內置的和配置的插件的apply方法
After-resolvers根據配置初始化resolver,resolver負責在文件系統中尋找指定路徑的文件

#### 1.3.2 編譯階段 (事件名全為小寫)

事件解釋
run啟動一次編譯
Watch-run在監聽模式下啟動編譯,文件發生變化會重新編譯
compile告訴插件一次新的編譯將要啟動,同時會給插件帶上compiler對象
compilation當webpack以開發模式運行時,每當檢測到文件的變化,便有一次新的compilation被創建。一個Compilation對象包含了當前的模塊資源、編譯生成資源、變化的文件等。compilation對象也提供了很多事件回調給插件進行拓展
make一個新的compilation對象創建完畢,即將從entry開始讀取文件,根據文件類型和編譯的loader對文件進行==編譯==,編譯完后再找出該文件依賴的文件,遞歸地編譯和解析
after-compile一次compilation執行完成
invalid當遇到錯誤會觸發改事件,該事件不會導致webpack退出


在編譯階段最重要的事件是compilation,因為在compilation階段調用了Loader,完成了每個模塊的==轉換==操作。在compilation階段又會發生很多小事件,如下表

事件解釋
build-module使用相應的Loader去轉換一個模塊
Normal-module-loader在使用loader轉換完一個模塊后,使用acorn解析轉換后的內容,輸出對應的抽象語法樹(AST),以方便webpack對代碼進行分析
program從配置的入口模塊開始,分析其AST,當遇到require等導入其他模塊的語句時,便將其加入依賴的模塊列表中,同時對于新找出來的模塊遞歸分析,最終弄清楚所有模塊的依賴關系
seal所有模塊及依賴的模塊都通過Loader轉換完成,根據依賴關系生成Chunk


2.3 輸出階段

輸出階段會發生的事件及解釋:

事件解釋
should-emit所有需要輸出的文件已經生成,詢問插件有哪些文件需要輸出,有哪些不需要輸出
emit確定好要輸出哪些文件后,執行文件輸出,==可以在這里獲取和修改輸出的內容==
after-mit文件輸出完畢
done成功完成一次完整的編譯和輸出流程
failed如果在編譯和輸出中出現錯誤,導致webpack退出,就會直接跳轉到本步驟,插件可以在本事件中獲取具體的錯誤原因

在輸出階段已經得到了各個模塊經過轉化后的結果和其依賴關系,并且將相應的模塊組合在一起形成一個個chunk.在輸出階段根據chunk的類型,使用對應的模板生成最終要輸出的文件內容. |

//以下代碼用來包含webpack運行過程中的每個階段
//file:webpack.config.js

const path = require('path');
//插件監聽事件并執行相應的邏輯
class TestPlugin {
  constructor() {
    console.log('@plugin constructor');
  }

  apply(compiler) {
    console.log('@plugin apply');

    compiler.plugin('environment', (options) => {
      console.log('@environment');
    });

    compiler.plugin('after-environment', (options) => {
      console.log('@after-environment');
    });

    compiler.plugin('entry-option', (options) => {
      console.log('@entry-option');
    });

    compiler.plugin('after-plugins', (options) => {
      console.log('@after-plugins');
    });

    compiler.plugin('after-resolvers', (options) => {
      console.log('@after-resolvers');
    });

    compiler.plugin('before-run', (options, callback) => {
      console.log('@before-run');
      callback();
    });

    compiler.plugin('run', (options, callback) => {
      console.log('@run');
      callback();
    });

    compiler.plugin('watch-run', (options, callback) => {
      console.log('@watch-run');
      callback();
    });

    compiler.plugin('normal-module-factory', (options) => {
      console.log('@normal-module-factory');
    });

    compiler.plugin('context-module-factory', (options) => {
      console.log('@context-module-factory');
    });

    compiler.plugin('before-compile', (options, callback) => {
      console.log('@before-compile');
      callback();
    });

    compiler.plugin('compile', (options) => {
      console.log('@compile');
    });

    compiler.plugin('this-compilation', (options) => {
      console.log('@this-compilation');
    });

    compiler.plugin('compilation', (options) => {
      console.log('@compilation');
    });

    compiler.plugin('make', (options, callback) => {
      console.log('@make');
      callback();
    });

    compiler.plugin('compilation', (compilation) => {

      compilation.plugin('build-module', (options) => {
        console.log('@build-module');
      });

      compilation.plugin('normal-module-loader', (options) => {
        console.log('@normal-module-loader');
      });

      compilation.plugin('program', (options, callback) => {
        console.log('@program');
        callback();
      });

      compilation.plugin('seal', (options) => {
        console.log('@seal');
      });
    });

    compiler.plugin('after-compile', (options, callback) => {
      console.log('@after-compile');
      callback();
    });

    compiler.plugin('should-emit', (options) => {
      console.log('@should-emit');
    });

    compiler.plugin('emit', (options, callback) => {
      console.log('@emit');
      callback();
    });

    compiler.plugin('after-emit', (options, callback) => {
      console.log('@after-emit');
      callback();
    });

    compiler.plugin('done', (options) => {
      console.log('@done');
    });

    compiler.plugin('failed', (options, callback) => {
      console.log('@failed');
      callback();
    });

    compiler.plugin('invalid', (options) => {
      console.log('@invalid');
    });

  }
}
#在目錄下執行
webpack
#輸出以下內容
@plugin constructor
@plugin apply
@environment
@after-environment
@entry-option
@after-plugins
@after-resolvers
@before-run
@run
@normal-module-factory
@context-module-factory
@before-compile
@compile
@this-compilation
@compilation
@make
@build-module
@normal-module-loader
@build-module
@normal-module-loader
@seal
@after-compile
@should-emit
@emit
@after-emit
@done
Hash: 19ef3b418517e78b5286
Version: webpack 3.11.0
Time: 95ms
    Asset     Size  Chunks             Chunk Names
bundle.js  3.03 kB       0  [emitted]  main
   [0] ./main.js 44 bytes {0} [built]
   [1] ./show.js 114 bytes {0} [built]

2 輸出文件分析

2.1 舉個栗子

下面通過 Webpack 構建一個采用 CommonJS 模塊化編寫的項目,該項目有個網頁會通過 JavaScript 在網頁中顯示 Hello,Webpack

運行構建前,先把要完成該功能的最基礎的 JavaScript 文件和 HTML 建立好,需要如下文件:

頁面入口文件 index.html

<html>
<head>
  <meta charset="UTF-8">
</head>
<body>
<p id="app"></p>
<!--導入 Webpack 輸出的 JavaScript 文件-->
<script src="./dist/bundle.js"></script>
</body>
</html>

JS 工具函數文件 show.js

// 操作 DOM 元素,把 content 顯示到網頁上
function show(content) {
  window.document.getElementById('app').innerText = 'Hello,' + content;
}

// 通過 CommonJS 規范導出 show 函數
module.exports = show;

JS 執行入口文件 main.js

// 通過 CommonJS 規范導入 show 函數
const show = require('./show.js');
// 執行 show 函數
show('Webpack');

Webpack 在執行構建時默認會從項目根目錄下的 webpack.config.js 文件讀取配置,所以你還需要新建它,其內容如下:

const path = require('path');

module.exports = {
  // JavaScript 執行入口文件
  entry: './main.js',
  output: {
    // 把所有依賴的模塊合并輸出到一個 bundle.js 文件
    filename: 'bundle.js',
    // 輸出文件都放到 dist 目錄下
    path: path.resolve(__dirname, './dist'),
  }
};

由于 Webpack 構建運行在 Node.js 環境下,所以該文件最后需要通過 CommonJS 規范導出一個描述如何構建的 Object 對象。

|-- index.html
|-- main.js
|-- show.js
|-- webpack.config.js

一切文件就緒,在項目根目錄下執行 webpack 命令運行 Webpack 構建,你會發現目錄下多出一個 dist目錄,里面有個 bundle.js 文件, bundle.js 文件是一個可執行的 JavaScript 文件,它包含頁面所依賴的兩個模塊 main.jsshow.js 及內置的 webpackBootstrap 啟動函數。 這時你用瀏覽器打開 index.html 網頁將會看到 Hello,Webpack

2.2 bundle.js文件做了什么

看之前記住:一個模塊就是一個文件,

首先看下bundle.js長什么樣子:

webpack原理的示例分析

注意:序號1處是個自執行函數,序號2作為自執行函數的參數傳入

具體代碼如下:(建議把以下代碼放入編輯器中查看,最好讓index.html執行下,弄清楚執行的順序)

(function(modules) { // webpackBootstrap
  // 1. 緩存模塊
  var installedModules = {};
  // 2. 定義可以在瀏覽器使用的require函數
  function __webpack_require__(moduleId) {

    // 2.1檢查模塊是否在緩存里,在的話直接返回
    if(installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    // 2.2 模塊不在緩存里,新建一個對象module=installModules[moduleId] {i:moduleId,l:模塊是否加載,exports:模塊返回值}
    var module = installedModules[moduleId] = {
      i: moduleId,//第一次執行為0
      l: false,
      exports: {}
    };//第一次執行module:{i:0,l:false,exports:{}}
    // 2.3 執行傳入的參數中對應id的模塊 第一次執行數組中傳入的第一個參數
          //modules[0].call({},{i:0,l:false,exports:{}},{},__webpack_require__函數)
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    // 2.4 將這個模塊標記為已加載
    module.l = true;
    // 2.5 返回這個模塊的導出值
    return module.exports;
  }
  // 3. webpack暴露屬性 m c d n o p
  __webpack_require__.m = modules;
  __webpack_require__.c = installedModules;
  __webpack_require__.d = function(exports, name, getter) {
    if(!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        configurable: false,
        enumerable: true,
        get: getter
      });
    }
  };
  __webpack_require__.n = function(module) {
    var getter = module && module.__esModule ?
      function getDefault() { return module['default']; } :
      function getModuleExports() { return module; };
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };
  __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  __webpack_require__.p = "";
  // 4. 執行reruire函數引入第一個模塊(main.js對應的模塊)
  return __webpack_require__(__webpack_require__.s = 0);
})
([ // 0. 傳入參數,參數是個數組

  /* 第0個參數 main.js對應的文件*/
  (function(module, exports, __webpack_require__) {

    // 通過 CommonJS 規范導入 show 函數
    const show = __webpack_require__(1);//__webpack_require__(1)返回show
    // 執行 show 函數
    show('Webpack');

  }),
  /* 第1個參數 show.js對應的文件 */
  (function(module, exports) {

    // 操作 DOM 元素,把 content 顯示到網頁上
    function show(content) {
      window.document.getElementById('app').innerText = 'Hello,' + content;
    }
    // 通過 CommonJS 規范導出 show 函數
    module.exports = show;

  })
]);

以上看上去復雜的代碼其實是一個自執行函數(文件作為自執行函數的參數),可以簡寫如下:

(function(modules){
    //模擬require語句
    function __webpack_require__(){}
    //執行存放所有模塊數組中的第0個模塊(main.js)
    __webpack_require_[0]
})([/*存放所有模塊的數組*/])

bundles.js能直接在瀏覽器中運行的原因是,在輸出的文件中通過__webpack_require__函數,定義了一個可以在瀏覽器中執行的加載函數(加載文件使用ajax實現),來模擬Node.js中的require語句。

原來一個個獨立的模塊文件被合并到了一個單獨的 bundle.js 的原因在于瀏覽器不能像 Node.js 那樣快速地去本地加載一個個模塊文件,而必須通過網絡請求去加載還未得到的文件。 如果模塊數量很多,加載時間會很長,因此把所有模塊都存放在了數組中,執行一次網絡加載。

修改main.js,改成import引入模塊

import show from './show';
show('Webpack');

在目錄下執行webpack,會發現:

  1. 生成的代碼會有所不同,但是主要的區別是自執行函數的參數不同,也就是2.2代碼的第二部分不同

([//自執行函數和上面相同,參數不同
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__show__ = __webpack_require__(1);

Object(__WEBPACK_IMPORTED_MODULE_0__show__["a" /* default */])('Webpack');


}),
/* 1 */
(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = show;
function show(content) {
  window.document.getElementById('app').innerText = 'Hello,' + content;
}


})
]);

參數不同的原因是es6的import和export模塊被webpack編譯處理過了,其實作用是一樣的,接下來看一下在main.js中異步加載模塊時,bundle.js是怎樣的

2.3異步加載時,bundle.js代碼分析

main.js修改如下

import('./show').then(show=>{
    show('Webpack')
})

構建成功后會生成兩個文件

  1. bundle.js  執行入口文件

  2. 0.bundle.js 異步加載文件

其中0.bundle.js文件的內容如下:

webpackJsonp(/*在其他文件中存放的模塊的ID*/[0],[//本文件所包含的模塊
/* 0 */,
/* 1 show.js對應的模塊 */
(function(module, __webpack_exports__, __webpack_require__) {

  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
  /* harmony export (immutable) */ 
  __webpack_exports__["default"] = show;

  function show(content) {
    window.document.getElementById('app').innerText = 'Hello,' + content;
  }

})
]);

bundle.js文件的內容如下:

注意:bundle.js比上面的bundle.js的區別在于:

  1. 多了一個__webpack_require__.e,用于加載被分割出去的需要異步加載的chunk對應的文件

  2. 多了一個webpackJsonp函數,用于從異步加載的文件中安裝模塊

(function(modules) { // webpackBootstrap
    // install a JSONP callback for chunk loading
  var parentJsonpFunction = window["webpackJsonp"];
  // webpackJsonp用于從異步加載的文件中安裝模塊
  // 將webpackJsonp掛載到全局是為了方便在其他文件中調用
  /**
   * @param chunkIds 異步加載的模塊中需要安裝的模塊對應的id
   * @param moreModules 異步加載的模塊中需要安裝模塊列表
   * @param executeModules 異步加載的模塊安裝成功后需要執行的模塊對應的index
   */
    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
        // add "moreModules" to the modules object,
        // then flag all "chunkIds" as loaded and fire callback
        var moduleId, chunkId, i = 0, resolves = [], result;
        for(;i < chunkIds.length; i++) {
            chunkId = chunkIds[i];
            if(installedChunks[chunkId]) {
                resolves.push(installedChunks[chunkId][0]);
            }
            installedChunks[chunkId] = 0;
        }
        for(moduleId in moreModules) {
            if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
                modules[moduleId] = moreModules[moduleId];
            }
        }
        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
        while(resolves.length) {
            resolves.shift()();
        }
    };
    // The module cache
    var installedModules = {};
    // objects to store loaded and loading chunks
    var installedChunks = {
        1: 0
    };
    // The require function
    function __webpack_require__(moduleId) {
        // Check if module is in cache
        if(installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        // Create a new module (and put it into the cache)
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };
        // Execute the module function
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
        // Flag the module as loaded
        module.l = true;
        // Return the exports of the module
        return module.exports;
    }
    // This file contains only the entry chunk.
  // The chunk loading function for additional chunks
  /**
   * 用于加載被分割出去的需要異步加載的chunk對應的文件
   * @param chunkId 需要異步加載的chunk對應的id
   * @returns {Promise}
   */
    __webpack_require__.e = function requireEnsure(chunkId) {
      var installedChunkData = installedChunks[chunkId];
      if(installedChunkData === 0) {
        return new Promise(function(resolve) { resolve(); });
      }
      // a Promise means "currently loading".
      if(installedChunkData) {
        return installedChunkData[2];
      }
      // setup Promise in chunk cache
      var promise = new Promise(function(resolve, reject) {
        installedChunkData = installedChunks[chunkId] = [resolve, reject];
      });
      installedChunkData[2] = promise;
      // start chunk loading
      var head = document.getElementsByTagName('head')[0];
      var script = document.createElement('script');
      script.type = "text/javascript";
      script.charset = 'utf-8';
      script.async = true;
      script.timeout = 120000;
      if (__webpack_require__.nc) {
        script.setAttribute("nonce", __webpack_require__.nc);
      }
      script.src = __webpack_require__.p + "" + chunkId + ".bundle.js";
      var timeout = setTimeout(onScriptComplete, 120000);
      script.onerror = script.onload = onScriptComplete;
      function onScriptComplete() {
        // avoid mem leaks in IE.
        script.onerror = script.onload = null;
        clearTimeout(timeout);
        var chunk = installedChunks[chunkId];
        if(chunk !== 0) {
          if(chunk) {
            chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
          }
          installedChunks[chunkId] = undefined;
        }
      };
      head.appendChild(script);
      return promise;
    };
    // expose the modules object (__webpack_modules__)
    __webpack_require__.m = modules;
    // expose the module cache
    __webpack_require__.c = installedModules;
    // define getter function for harmony exports
    __webpack_require__.d = function(exports, name, getter) {
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, {
                configurable: false,
                enumerable: true,
                get: getter
            });
        }
    };
    // getDefaultExport function for compatibility with non-harmony modules
    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };
    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    // __webpack_public_path__
    __webpack_require__.p = "";
    // on error function for async loading
    __webpack_require__.oe = function(err) { console.error(err); throw err; };
    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 0);
})
/************************************************************************/
([//存放沒有經過異步加載的,隨著執行入口文件加載的模塊
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 1)).then(show=>{
    show('Webpack')
})


/***/ })
]);

感謝各位的閱讀!關于webpack原理的示例分析就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

尚志市| 师宗县| 调兵山市| 绥芬河市| 芜湖县| 江安县| 翁牛特旗| 庆城县| 沂源县| 嫩江县| 沿河| 灵璧县| 台中市| 龙口市| 陆川县| 关岭| 黑河市| 九江县| 福安市| 淅川县| 格尔木市| 沽源县| 报价| 霍山县| 兴义市| 平昌县| 工布江达县| 康定县| 焦作市| 体育| 隆德县| 襄樊市| 闸北区| 抚顺县| 安达市| 江西省| 饶阳县| 永德县| 驻马店市| 商南县| 佛山市|