Cesium官方教程13--Cesium和Webpack

原文地址:https://cesiumjs.org/tutorials/cesium-and-webpack/

Cesium 和 Webpack

Webpack是非常強大非常流行的JavaScript 模塊打包工具。它可以讓開發(fā)人員以一種簡單直觀的 require 方式去加載各種頁面需要的文件,極大的方便了開源人員對代碼和資源文件進行結構化設計。當編譯的時候,它會跟蹤代碼依賴性,把所有的模型打包到瀏覽器可以直接加載的一個或者多個bundles中。

在這個教程的前一半,我們創(chuàng)建一個簡單的web項目,學會使用webpack,然后再去集成 Cesium npm模塊。這是基于Cesium開發(fā)正式web項目的很好開端,但是它不是學習Cesium的最簡單示例,可以看一下我們的新手入門

在教程的后半部分,我們將討論更多高級的webpack 配置參數(shù),去優(yōu)化使用Cesium的項目。

這個優(yōu)化Cesium和Webpack集成的項目示例,可以查看官網(wǎng)webpack示例 代碼庫。

先決條件

  • 對命令行,JavaScript語言和web開發(fā)需要有一個基本了解。
  • 一個代碼編輯器(IDE)。Cesium團隊的開發(fā)人員都用 Webstorm, 但是 Sublime Text 等工具也可以。
  • 安裝了Node.js 。LTS版本最好,但是只要是v6以上都可以的。

創(chuàng)建一個基本的 webpack 程序

第一部分,描述如何建立一個簡本的webpack程序以及一個開發(fā)測試服務器。如果你已經(jīng)創(chuàng)建了程序,就想添加Cesium,直接看后面和Cesium繼承部分。

使用npm初始化程序

創(chuàng)建一個程序目錄,命名為cesium-webpack-app。打開控制臺,定位到該目錄下,運行下面的命令:

npm init

根據(jù)提示信息,填寫你的項目里的詳細信息。一路按enter使用默認配置也是可以的。完成后,這個目錄下創(chuàng)建了一個 package.json文件。

編寫自己的程序代碼

在這個目錄下,創(chuàng)建一個源碼目錄src。這個目錄是我們項目源碼所在位置,我們編輯的文件都放這下面。當我們編譯后,webpack會自動生成編譯后文件放在 dist目錄下。
我們創(chuàng)建一個 src/index.html文件,用一個HTML頁面的模板。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>Hello World!</title>
  </head>
  <body>
    <p>Hello World!</p>
  </body>
</html>

現(xiàn)在我們需要添加我們程序的入口 。這個入口會告訴webpack去把所有依賴的源碼文件打包。打包后的文件才會在 index.html文件里引用。
現(xiàn)在,為了簡單,我們新建 src/index.js文件,只添加下面這句確認環(huán)境配置好了。

console.log('Hello World!');

安裝和配置webpack

下來我們安裝webpack。

npm install --save-dev webpack
webpack安裝
配置參數(shù)

創(chuàng)建一個名為webpack.config.js的文件。這是定義webpack的配置參數(shù) 的地方,它會傳給webpack的編譯器。

const path = require('path');
const webpack = require('webpack');
module.exports = {
    context: __dirname,
    entry: {
        app: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    }
};

首先,我們需要從Nodejs中加載 path和我們剛剛安裝的webpack模塊。我們告訴webpack,context的及處理路徑是Node的全局變量__dirname,這個全局變量指的這個文件所在的目錄位置。我們設定了我們的程序入口src/index.js,而且把它命名為app。我們還告訴webpack把打包后的文件(這個文件的名稱叫app.js,因為我們設置了程序入口的[name])輸出到dist目錄下。最后export 把這個配置對象導出,其他文件才能用到這個配置。

加載器(Loaders)

我們還需要加載我們的css以及其他資源文件的方法。webpack允許我們像加載js模塊一樣,載入這些文件。我們需要使用 loaders。我們需要使用 style-loader, css-loader, 和 url-loader。

npm install --save-dev style-loader css-loader url-loader
loader安裝

繼續(xù)往 webpack.config.js里添加 module.rules。我們添加兩個規(guī)則,一個是為css文件,一個是為其他靜態(tài)文件。每個一個配置,我們定義 test過濾器來篩選這種類似的文件,還定義了一個use數(shù)組,指定這種類型用到的loader。

const path = require('path');

const webpack = require('webpack');

module.exports = {
    context: __dirname,
    entry: {
        app: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]
        }, {
            test: /\.(png|gif|jpg|jpeg|svg|xml|json)$/,
            use: [ 'url-loader' ]
        }]
    }
};
插件

項目已經(jīng)創(chuàng)建好了,下來需要修改index.html ,讓它把打包文件引到頁面上。使用webpack的 插件(plugins) 中的 html-webpack-plugin。

npm install --save-dev html-webpack-plugin
安裝plugin

webpack.config.js 文件的最開始加載這個插件,并添加到 plugins配置里去。把 src/index.html 做為一個template設置,webpack就會自動在這個頁面注入一個我們的打包文件的引用。

const path = require('path');

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    context: __dirname,
    entry: {
        app: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]
        }, {
            test: /\.(png|gif|jpg|jpeg|svg|xml|json)$/,
            use: [ 'url-loader' ]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        })
    ]
};

注意,這個配置文件也是一個普通的JavaScript文件,所以我們可以引用任何Nodejs的模塊,做任何操作。

打包程序

package.json文件里添加一點腳本配置,我們就能很方便的調用npm。把里面默認的"test"腳本刪除掉,我們用不上它。添加一個build命令。

  "scripts": {
    "build": "node_modules/.bin/webpack --config webpack.config.js"
  }

這個腳本就是簡單的調用了webpack命令,并把我們剛才新建的配置文件webpack.config.js 當作參數(shù)傳遞過去。

因為我們的webpack剛才是本地安裝。這樣每個項目都使用獨立的版本,這也是webpack的推薦安裝方式。如果你選擇全局安裝方式,使用 npm install --global webpack全局安裝webpack,然后這里的build命令修改為 webpack --config webpack.config.js。
我們運行下編譯命令。

npm run build

你應該可以看到webpack開始輸出了一些信息:

npm run build

> test-app@1.0.0 build C:\workspace\test-app
> node_modules/.bin/webpack --config webpack.config.js

Hash: 2b42bff7a022b5d956a9
Version: webpack 3.6.0
Time: 2002ms
                                        Asset       Size  Chunks                    Chunk Names
     Assets/Textures/NaturalEarthII/2/0/3.jpg    10.3 kB          [emitted]
                                       app.js     162 kB       0  [emitted]         app

譯者機器上的輸出

我們的打包文件app.jsindex.html 輸出到了 dist目錄。這些文件已經(jīng)是可以使用的程序了。
編譯后的目錄

啟動開發(fā)服務器

這個還不算什么。我們使用 webpack-dev-server 快速的搭建一個開發(fā)者服務器,我們的程序就可以實時查看了。
首先安裝webpack-dev-server

npm install --save-dev webpack-dev-server

下來,在 package.json文件中增加另外的腳本。使用start命令去啟動服務,通過 --config參數(shù)傳遞參數(shù)。還傳一個 --open去自動在瀏覽器打開我們的頁面。

  "scripts": {
    "build": "node_modules/.bin/webpack --config webpack.config.js",
    "start": "node_modules/.bin/webpack-dev-server --config webpack.config.js --open"
  }

我們需要告訴服務器去哪里尋找文件。這個需要和我們的webpack輸出目錄一致,也就是 dist目錄。所以在 webpack.config.js文件中增加一些配置。

    // 開發(fā)服務器配置
    devServer: {
        contentBase: path.join(__dirname, "dist")
    }

最后,我們運行程序

npm start

你應該能看見程序已經(jīng)在 localhost:8080運行了。如果你打開瀏覽器控制臺,因會看見“Hello World!” 已經(jīng)輸出了。

基本webpack程序示例
瀏覽器命令行

webpack 程序里集成 Cesium

我們的webpack程序已經(jīng)搭好了框架,下來我們干點有意思的,增加Cesium。

安裝Cesium

從npm中安裝 cesium 模塊,在 package.json 中增加開發(fā)依賴選項。

npm install --save-dev cesium

webpack中配置Cesium

Cesium是一個龐大的復雜的庫。除了JavaScript模塊之外,它還包含靜態(tài)資源css,圖片和json文件。它也包含為了多線程進行大量計算的web worker。和傳統(tǒng)npm模塊不同,Cesium也沒有定義一個入口,因為這個庫可以各種方式應用。為了正確使用,我們需要配置一些額外選項。
首先,我們定義Cesium所在位置。我們使用源碼,這樣我們就可以使用獨立模塊,依靠webpack可以跟蹤依賴性。其他方式是使用編譯好的(最小化壓縮或者未壓縮)版本的Cesium;可是,那些模塊已經(jīng)被RequireJS optimizer 組合和優(yōu)化,這種方式集成起來簡單避免犯錯,但是對于我們的優(yōu)化靈活度不夠好。

webpack.config.js,我們增加如下配置

// Cesium源碼所在目錄
const cesiumSource = 'node_modules/cesium/Source';
const cesiumWorkers = '../Build/Cesium/Workers';

我們選擇使用了npm 中的模塊,安裝容易一些,當然你也可以用 GitHub 庫 上的版本,或者解壓已發(fā)布版本下載。這時候僅僅需要更改cesiumSource 指向Cesium的 Source 目錄,相對 webpack.config.js 文件的相對路徑。
接著再增加一些配置項,為了解決使用webpack編譯Cesium的一些問題。

    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
        //需要編譯Cesium中的多行字符串 
        sourcePrefix: ''
    },
    amd: {
        //允許Cesium兼容 webpack的require方式 
        toUrlUndefined: true
    },
    node: {
        // 解決fs模塊的問題(Resolve node module use of fs)
        fs: 'empty'
    },

快速過下,為什么這些配置選項需要配置。

  • output.sourcePrefix: '' 因為某些版本的webpack默認會在輸出的每一行的開始增加一個\t 字符。Cesium有很多多行字符串,所以我們需要使用空字符串 ''來覆蓋這個選項。
  • amd.toUrlUndefined: true 告訴Cesium,webpack中計算 require聲明的AMD 模塊里的toUrl 函數(shù)和標準的不兼容。
  • node.fs: 'empty' 解決一些第三方庫使用的fs 模塊,它一般是用在NodeJS的環(huán)境里,而不能在瀏覽器環(huán)境里使用。
    下來我們增加一個 cesium 別名(alias) ,我們就很容易的在項目里引用,就像一個傳統(tǒng)的Node 模塊。
    resolve: {
        alias: {
            // Cesium模塊名稱
            cesium: path.resolve(__dirname, cesiumSource)
        }
    },

</figure>

管理Cesium 靜態(tài)文件

最后,我們確認一下Cesium的靜態(tài)資源,控件,web worker文件能被服務正確加載。
我們使用 copy-webpack-plugin,它能在編譯階段,把Cesium里靜態(tài)文件整個拷貝到 dist 目錄下,確保我們的服務能訪問它。首先,安裝它。

npm install --save-dev copy-webpack-plugin

下來在webpack.config.js文件最開始require它。

const CopywebpackPlugin = require('copy-webpack-plugin');

此外,在plugins 目錄下增加下述配置:

    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }),
        // 拷貝Cesium 資源、控價、web worker到靜態(tài)目錄 
        new CopywebpackPlugin([ { from: path.join(cesiumSource, cesiumWorkers), to: 'Workers' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Assets'), to: 'Assets' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Widgets'), to: 'Widgets' } ])
    ],

我們直接拷貝了AssetsWidgets目錄。也拷貝了編譯好的 web worker腳本,他們已經(jīng)使用 RequireJS optimizer編譯和優(yōu)化過了。因為web woker設計就是運行在他們自己的線程里,所以他們可以直接載入和運行。web worker很少需要調試他們原來的代碼。所以直接整個從 Build/Cesium/Workers目錄拷貝過去。
如果你用的GitHub庫的源碼,那么這個Build目錄不存在。確認你定位到了Cesium的根目錄下,然后運行 npm run release去編譯輸出。更多信息可以查看Cesium編譯指導
最后,我們使用DefinePlugin 定義了一個環(huán)境變量,這個告訴Cesium加載靜態(tài)文件的URL根路徑,并把它編譯到webpack里去。最后plugins 的配置數(shù)組應該是這樣的:

    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CopywebpackPlugin([ { from: path.join(cesiumSource, cesiumWorkers), to: 'Workers' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Assets'), to: 'Assets' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Widgets'), to: 'Widgets' } ]),
        new webpack.DefinePlugin({
            //Cesium載入靜態(tài)的資源的相對路徑
            CESIUM_BASE_URL: JSON.stringify('')
        })
    ],

在我們的程序里引用Cesium

現(xiàn)在我們的程序里引用Cesium的獨立模塊有幾種方式。使用CommonJS的語法,以及最新的ES6的 import模塊聲明。
另外,你也可以通過一個 Cesium 引入整個Cesium庫(例如,我們在 Sandcastle中所用的方式)。你也可以只請求某個你需要的特定模塊。因為Cesium是一個巨大的庫,這種方式讓你只引用你用的特定模塊,而不是整個Cesium庫。

CommonJS 形式的 require

引用Cesium整個庫:

var Cesium = require('cesium/Cesium');
var viewer = new Cesium.Viewer('cesiumContainer');

引用某個Cesium庫:

var Color = require('cesium/Core/Color');
var color = Color.fromRandom();
ES6 形式的 import

引用Cesium整個庫:

import Cesium from 'cesium/Cesium';
var viewer = new Cesium.Viewer('cesiumContainer');

引用某個Cesium庫:

import Color from 'cesium/core/Color';
var color = Color.fromRandom();

請求靜態(tài)資源文件

webpack的設計理念就是每個文件都當作一個模塊。所以導入靜態(tài)資源和導入js模塊完全相同。我們已經(jīng)在loaders里面配置了,告訴webpack每種類型的文件使用哪個loader,所以我們只需要調用 require

require('cesium/Widgets/widgets.css');

Hello World! 示例

有了基本框架也學習如何引入Cesium文件,我們來創(chuàng)建一個簡單的Hello World程序。
再看下index.js文件。刪除它的內容,首先我們引入 Cesium,然后定義 Cesium的對象:

var Cesium = require('cesium/Cesium');

為了使用Cesium Viewer里的控件,我們還需要引入Cesium的CSS:

require('cesium/Widgets/widgets.css');

在HTML的body部分,我們創(chuàng)建了一個viewer需要的div。在 index.html 文件里,刪除 <p>Hello World!</p> 這一行,替換成下面:

<div id="cesiumContainer"></div>

最后,我們創(chuàng)建一個viewer的實例。返回到 index.js 文件,添加下面代碼:

var viewer = new Cesium.Viewer('cesiumContainer');

當我們運行 npm start ,我們將看見瀏覽器里的Cesium控件了。

Hello World 程序

為了美觀,我們用一些自定義css去掉頁面上的白色邊界。
創(chuàng)建一個新的文件src/css/main.css,增加下面的樣式:

html, body, #cesiumContainer {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden;
}

index.js里require它,在其他require的后面。

require('./css/main.css');

重新npm start,Cesium的控件完美全屏了。

隨便拷貝粘貼 Sandcastle 中的示例。 我認為 粒子系統(tǒng)的示例 最能說明問題。

示例程序

webpack 高級配置

webpack有很多其他方法增加性能、減小打包大小、執(zhí)行額外的或者復雜的編譯過程。對于使用Cesium庫,我們將討論一些配置選項。
我們的最佳配置選項示例放在github的配置庫里 webpack.release.config.js

代碼切片

webpack默認會把Cesium和整個程序代碼打進一個 chunk中,最終這個庫非常龐大。我們也可以用CommonChunksPlugin把Cesium打到它自己的包,提升程序的性能。如果最終你的程序創(chuàng)建了多個chunks,他們可以引用一個通用的cesiumchunk。
只需要在 webpack.config.js添加這個插件,然后設置打斷Cesium模塊的規(guī)則。

    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'cesium',
            minChunks: module => module.context && module.context.indexOf('cesium') !== -1
        })
    ]

這個插件最新版webpack上已經(jīng)不能這么配置了,使用config.optimization.splitChunks去設置,詳情點擊webpack-splitChunks

啟用 source maps

Source maps 允許webpack跟蹤原來代碼中的錯誤,它有很多配置項。它用編譯速度來換取或多或少的調試詳情信息。我們推薦使用'eval'選項,但是盡量使用你自己開發(fā)中最好的方式。

    devtool: 'eval'

不推薦在最終產(chǎn)品代碼里添加Source maps(容易被破解唄)。

刪除 pragmas

Cesium源碼里包含了一些開發(fā)錯誤和警告信息,但是產(chǎn)品中是不需要的。通常我們使用 RequireJS Optimizer在release編譯下壓縮它。因為沒有webpack內置的方式去刪除這些警告,我們將使用 strip-pragma-loader。
首先,安裝。

npm install strip-pragma-loader --save-dev

然后在loader的 module.rules增加規(guī)則,并且 把 debug 設置為 false.

    rules: [{
        // 刪除 cesium pragmas
        test: /\.js$/,
            enforce: 'pre',
            include: path.resolve(__dirname, cesiumSource),
            use: [{
                loader: 'strip-pragma-loader',
                options: {
                    pragmas: {
                        debug: false
                    }
                }
            }]
    }]

混淆和壓縮

混淆和壓縮代碼使最終產(chǎn)品的代碼變得更小。對于一個release編譯,Cesium會JavaScript文件,并且壓縮CSS文件。為了混淆Cesium源碼,我們使用 uglifyjs-webpack-plugin插件。

npm install uglifyjs-webpack-plugin --save-dev

引入這個插件

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

在插件里引用它

    plugins: [
        new webpack.optimize.UglifyJsPlugin()
    ]

這個插件最新版webpack上已經(jīng)不能這么配置了,使用config.optimization.minimize去設置,詳情點擊webpack-optimization
為了壓縮css代碼,在css-loader中使用minimize選項。

    module: {
        rules: [{
            test: /\.css$/,
            use: [
                'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        // minify loaded css
                        minimize: true
                    }
                }
            ]
        }]
    }

相關資源

官方 cesium-webpack-示例 的git庫。包含最小的webpack配置,以及此篇教程的hello word代碼,還有部分可選的代碼配置選項。

學習如何基于Cesium開發(fā)項目實例,比如如何添加數(shù)據(jù)和配置樣式,那么去學習 Cesium Workshop 教程

玩下 Sandcastle以及官方用戶手冊。

深入學習webpack,可以看下webpack 概念 ,或者看下webpack的配置手冊 。

中國最專業(yè)的Cesium開發(fā)者社區(qū)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容