摘要:關于這個單頁面應用大家可以直接去我的上查看,我將結合這個項目去介紹。
這篇文章將介紹如何利用 webpack 進行單頁面應用的開發(fā),算是我在實際開發(fā)中的一些心得和體會,在這里給大家做一個分享。webpack 的介紹這里就不多說了,可以直接去官網(wǎng)查看。 關于這個單頁面應用大家可以直接去我的github上查看https://github.com/huangshuwei/webpackForSPA,我將結合這個項目去介紹。如果大家覺得這篇文章有不妥的地方,還請指出。
項目目錄這篇文章的目的是解決我們在開發(fā)中會遇到的問題,不是一篇基礎教程,還請諒解。
我將根據(jù)這個目錄結構進行講解
dist:發(fā)布的文件目錄,即webpack編譯輸出的目錄
libs:放置公共的文件,如js、css、img、font等
mockServer:模擬后端服務,即用webpack開發(fā)時模擬調(diào)用的后端服務(用nodejs服務模擬)
node_modules:項目依賴的包
src:資源文件,里面包含css、font、html、img、js
package.json:項目配置
webpack.config.js:webpack的配置文件
項目的使用建議先運行一下這個項目,有一個大致的了解,再往下閱讀。使用說明:
首先克隆一份到你的本地 $ git clone https://github.com/huangshuwei/webpackForSPA.git 然后 cd 到 ‘webpackForSPA’目錄下 $ cd webpackForSPA 接著你可以運行不同的命令查看結果 發(fā)布模式: $ npm run build 開發(fā)模式: $ npm run dev 熱更新模式 $ npm run dev-hrm 如果使用了熱更新模式,并且想要結合后端服務形式運行,那么cd 到‘mockServer’目錄下,并執(zhí)行node 服務: $ cd mockServer $ node server.js區(qū)分開發(fā)、熱更新、發(fā)布模式
一般開發(fā)時和發(fā)布時是不同的,比如開發(fā)時文件的訪問目錄包含‘dist’目錄,但是發(fā)布上線時,一般會把‘dist’文件夾去掉。
當然還有其他的一些細節(jié)不同。
開發(fā)模式:
能看到webpack編譯輸出的文件
js、css、html文件不需要壓縮
可以正確的運行編譯輸出后的文件
這種模式一般只是用來看webpack編譯輸出后的文件是否正確
熱更新模式:
看不到webpack編譯輸出的文件
js、css、html文件不需要壓縮
更改完文件后無需重新編譯并自動刷新瀏覽器
可以結合后端服務開發(fā),避過瀏覽器同源策略,如結合java、.net服務等
發(fā)布模式:
能看到webpack編譯輸出的文件
js、css、html文件壓縮
文件的層級目錄不需要包含‘dist’目錄
我區(qū)分開發(fā)、熱更新、發(fā)布模式是通過配置‘package.json’文件的運行命令,有些人是通過創(chuàng)建多個不同的webpack的配置文件來達到想要的效果。
像這個項目就是使用了多個webpack的配置文件。
配置命令這是在 package.json 文件中配置的
// package.json 文件 ... "scripts": { "build": "webpack --profile --progress --colors --display-error-details", "dev": "webpack --display-modules --profile --progress --colors --display-error-details", "dev-hrm": "webpack-dev-server --config" }, ...
color 輸出結果帶彩色,比如:會用紅色顯示耗時較長的步驟
profile 輸出性能數(shù)據(jù),可以看到每一步的耗時
progress 輸出當前編譯的進度,以百分比的形式呈現(xiàn)
display-modules 默認情況下 node_modules 下的模塊會被隱藏,加上這個參數(shù)可以顯示這些被隱藏的模塊
display-error-details 輸出詳細的錯誤信息
webpack-dev-server 將會開啟熱更新
更多請參考官網(wǎng) cli
配置好了package.json文件,我們就可以這樣運行
// 開發(fā)模式 npm run dev // 熱更新模式 npm run dev-hrm // 發(fā)布模式 npm run build配置變量標識
配置完了命令,當我們運行不同的命令時,我們可以通過‘process.env.npm_lifecycle_event’去獲取當前運行的命令,根據(jù)不同的命令,我們可以按照自己的需要做相應的處理。比如開發(fā)模式時,允許開啟調(diào)試,靜態(tài)資源不要壓縮;發(fā)布模式時,不允許調(diào)試,靜態(tài)資源要壓縮。具體如下:
// webpack.config.js // 獲取當前運行的模式 var currentTarget = process.env.npm_lifecycle_event; var debug, // 是否是調(diào)試 devServer, // 是否是熱更新模式 minimize; // 是否需要壓縮 if (currentTarget == "build") { // 發(fā)布模式 debug = false, devServer = false, minimize = true; } else if (currentTarget == "dev") { // 開發(fā)模式 debug = true, devServer = false, minimize = false; } else if (currentTarget == "dev-hrm") { // 熱更新模式 debug = true, devServer = true, minimize = false; }基礎配置 配置路徑
為了方便我們頻繁使用路徑,如下配置
// webpack.config.js var PATHS = { // 發(fā)布目錄 publicPath: debug ? "/webpackForSPA/dist/" : "/webpackForSPA/", // 公共資源目錄 libsPath: path.resolve(process.cwd(), "./libs"), // src 資源目錄 srcPath: path.resolve(process.cwd(), "src"), }配置別名
webpack的別名的目的就是簡化我們的操作,引用資源時直接使用別名即可(和 seajs 里的別名用法一樣)。配置如下:
// webpack.config.js ... resolve:{ alias: { // js jquery: path.join(PATHS.libsPath, "js/jquery/jquery"), underscore: path.join(PATHS.libsPath, "js/underscore/underscore.js"), // css bootstrapcss: path.join(PATHS.libsPath, "css/bootstrap/bootstrap-3.3.5.css"), indexcss: path.join(PATHS.srcPath, "css/index.css"), } } ...配置webpack編譯入口
// webpack.config.js ... entry:{ // 入口 js index: "./src/js/index.js", // 公共js包含的文件 common: [ path.join(PATHS.libsPath, "js/jquery/jquery.js"), path.join(PATHS.libsPath, "js/underscore/underscore.js") ], } ...配置webpack編譯輸出
// webpack.config.js ... output:{ // 輸出目錄 path: path.join(__dirname, "dist"), // 發(fā)布后,資源的引用目錄 publicPath: PATHS.publicPath, // 文件名稱 filename: "js/[name].js", // 按需加載模塊時輸出的文件名稱 chunkFilename: "js/[name].js" } ...提取css到多帶帶的文件
當我們在js文件中通過require("")引用js時,webpack 默認會將css文件與當前js文件打包一起,但是這種方式會阻塞頁面的加載,因為css的執(zhí)行要等待js文件加載進來。所以我們會把css從js文件中提取出來,放到一個多帶帶的css文件中。這時我們要使用webpack的插件:extract-text-webpack-plugin,配置如下:
引入插件
// webpack.config.js var ExtractTextPlugin = require("extract-text-webpack-plugin");
配置 loader
// webpack.config.js ... loaders: [ { test: /.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader!postcss-loader") }, ... ] ...
配置 plugins
// webpack.config.js ... plugins:[ new ExtractTextPlugin("css/[name].css", {allChunks: true}), ... ] ...公共js打包
項目中,我們通常會有公共的js,比如 jquery、bootstrap、underscore 等,那么這時候我們需要將這些公共的js多帶帶打包。這時我們需要用webpack自帶的插件:
// webpack.config.js ... plugins:[ // 會把 ‘entry’ 定義的 common 對應的兩個js 打包為 ‘common.js’ new webpack.optimize.CommonsChunkPlugin("common", "js/[name].js", Infinity), ] ...資源添加版本號
項目上線后,資源的版本號十分重要。資源沒有版本號,即使重新發(fā)布,客戶端瀏覽器可能會把老的資源緩存下來,導致無法下載最新的資源。webpack 支持給資源添加版本號,不僅僅是js、css,甚至font、img都可以添加版本號。我們可以通過webpack中的‘chunkhash’來解決。
首先要了解下webpack 中 [hash]、[chunkhash]、[chunkhash:8]的區(qū)別。
[hash]:webpack編譯會產(chǎn)生一個hash值
[chunkhash]:每個模塊的hash值
[chunkhash:8]:取[chunkhash]的前8位
推薦發(fā)布模式使用版本號,其他模式無需使用,熱更新模式不支持‘chunkhash’,但是支持‘hash’
資源加版本號,那么我們的輸出的部分都要做改動,并且要區(qū)分當前的命令模式,如下:
// webpack.config.js ... output:{ // 輸出目錄 path: path.join(__dirname, "dist"), // 發(fā)布后,資源的引用目錄 publicPath: PATHS.publicPath, // 文件名稱 filename: devServer ? "js/[name].js" : "js/[name]-[chunkhash:8].js", // 按需加載模塊時輸出的文件名稱 chunkFilename: devServer ? "js/[name].js" : "js/[name]-[chunkhash:8].js" } ...
輸出公共js的地方也要改動:
// webpack.config.js ... plugins:[ // 會把 ‘entry’ 定義的 common 對應的兩個js 打包為 ‘common.js’ new webpack.optimize.CommonsChunkPlugin("common", "" + (devServer ? "js/[name].js" : "js/[name]-[chunkhash:8].js"), Infinity), ] ...頁面自動引入含有版本號的文件
有個版本號后,我們考慮如何通過html引用這些含有版本號的js、css、font、img。webpack每次編譯后的資源 chunkhash 會隨著內(nèi)容的變化而變化,所以我們不可能每次都手動的更改html這些資源的引用路徑。這時我們要用到webpack的插件:html-webpack-plugin。這個插件的目的是生成html,也可以根據(jù)模板生成html,當然還有其他的功能,具體看插件介紹。下面是的配置:
引入插件
// webpack.config.js var HtmlWebpackPlugin = require("html-webpack-plugin");
配置 plugins,生成需要的html
// webpack.config.js ... plugins:[ new HtmlWebpackPlugin({ filename: "index.html", template: __dirname + "/src/index.html", inject: "true" }), new HtmlWebpackPlugin({ filename: "html/hrm.html", template: __dirname + "/src/html/hrm.html", inject: false, }), new HtmlWebpackPlugin({ filename: "html/home.html", template: __dirname + "/src/html/home.html", inject: false, }), ] ...
我們前面說過,webpack 默認只識別 js 文件,所以對于html也要使用對應的loader:
// webpack.config.js ... loaders:[ {test: /.html$/,loader: "html"}, ] ...引用圖片和字體
引用圖片和字體,需要對應的loader,并且可以設置這些資源大小的臨界值,當小于臨界值的時候,字體或者圖片文件會以base64的形式在html引用,否則則是以資源路徑的形式引用。如下:
// webpack.config.js // 圖片 loader { test: /.(png|gif|jpe?g)$/, loader: "url-loader", query: { /* * limit=10000 : 10kb * 圖片大小小于10kb 采用內(nèi)聯(lián)的形式,否則輸出圖片 * */ limit: 10000, name: "/img/[name]-[hash:8].[ext]" } }, // 字體loader { test: /.(eot|woff|woff2|ttf|svg)$/, loader: "url-loader", query: { limit: 5000, name: "/font/[name]-[hash:8].[ext]" } },資源文件的壓縮
js、css、html的壓縮是少不了的,webpack 自帶了壓縮插件,如果某些對象名稱不想被壓縮,可以排除不想要壓縮的對象名稱。配置如下:
// webpack.config.js ... plugins:[ new webpack.optimize.UglifyJsPlugin({ mangle: { // 排除不想要壓縮的對象名稱 except: ["$super", "$", "exports", "require", "module", "_"] }, compress: { warnings: false }, output: { comments: false, } }) ] ...使用jquery、underscore
通過webpack編譯輸出后的項目中,雖然頁面已經(jīng)引用了jquery、underscore,但是還是無法直接使用‘$’、‘_’對象,我們可以這樣:
var $ = require("jquery"); var _ = require("underscore");
但是這樣實在不方便,如果我們就是要使用‘$’、‘_’對象直接操作,webpack 內(nèi)置的插件可以幫我們解決。具體如下:
// webpack.config.js new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", "window.jQuery": "jquery", "_": "underscore", }),代碼分割,按需加載
在單頁面應用中,當我們加載其他的模板文件時,想要引用這個模板文件對應的js。如果我們通過這種方式require(),那么webpack會將這個模板文件對應的js也會和當前js打包成一個js。如果項目比較大,那么js文件也將越來越大。我們希望的是加載模板文件的時候動態(tài)的引用這個模板文件對應的js。那么我們可以通過 require.ensure()的方式。
比如現(xiàn)在有兩個導航菜單:
我們給這兩個菜單綁定點擊事件,當點擊‘home’時引用對應的‘home.js’;當點擊‘HRM’時引用對應的‘hrm.js’,那么大致可以這樣:
function loadJs(jsPath) { var currentMod; if (jsPath === "./home") { require.ensure([], function (require) { currentMod = require("./home"); }, "home"); } else if (jsPath === "./hrm") { require.ensure([], function (require) { currentMod = require("./hrm"); }, "hrm"); } }全局環(huán)境變量
有時我們只有在開發(fā)過程中,才想輸出log日志。可以用以下webpack內(nèi)置的插件解決:
// webpack.config.js ... plugins:[ new webpack.DefinePlugin({ // 全局debug標識 __DEV__: debug, }), ] ...
這時代碼中就可以這么寫了:
if (__DEV__) { console.log("debug 模式"); }清空發(fā)布目錄
發(fā)布前清空發(fā)布目錄是有必要的,我們可以通過‘clean-webpack-plugin’插件解決:
引入插件:
// webpack.config.js var CleanWebpackPlugin = require("clean-webpack-plugin");
配置plugins:
// webpack.config.js ... plugins:[ new CleanWebpackPlugin(["dist"], { root: "", // An absolute path for the root of webpack.config.js verbose: true,// Write logs to console. dry: false // Do not delete anything, good for testing. }), ] ...熱更新結合后端服務 熱更新
熱更新可以在你代碼改變的時候即時編譯輸出,不用每次都要從都重新編譯一遍,并且除了第一次編譯比較慢,后面的編譯都是增量編譯,速度很快。有了這個功能,我們就不需要,每次都從頭編譯一次了。配置如下:
// webpack.config.js ... plugins: [ // Enable multi-pass compilation for enhanced performance // in larger projects. Good default. new webpack.HotModuleReplacementPlugin({ multiStep: true }), ], devServer: { // Enable history API fallback so HTML5 History API based // routing works. This is a good default that will come // in handy in more complicated setups. historyApiFallback: true, // Unlike the cli flag, this doesn"t set // HotModuleReplacementPlugin! hot: true, inline: true, // Display only errors to reduce the amount of output. stats: "errors-only", host: "localhost", // Defaults to `localhost` process.env.HOST port: "8080", // Defaults to 8080 process.env.PORT } ...
這時我們只要打開瀏覽器,輸入:localhost:8080/ 就能看到結果,并且在你修改某些源文件后,瀏覽器會自動刷新,就能看到webpack 即時編譯輸出的結果,而不需要重新編譯。
結合后端服務我們在使用webpack開發(fā)時難免要結合后端服務開發(fā),比如我們用webstorm 編譯器開發(fā)項目,需要調(diào)用java的服務,由于有同源策略問題,這時我們會收到相關報錯信息。這時我們可以通過代理的方式繞過同源策略。
這里我用nodejs 模擬一個后端服務,如下:
// ~/mockServer/server.js var http = require("http"); var content = "▍if you see that,It means you have get the correct data by backend server(mock data by nodejs server)!"; var srv = http.createServer(function (req, res) { res.writeHead(200, {"Content-Type": "application/text"}); res.end(content); }); srv.listen(8888, function() { console.log("listening on localhost:8888"); });
接下來我們需要這樣配置去調(diào)用這個nodejs 的服務。
首先將熱更新配置的代碼修改為:
// webpack.config.js ... plugins: [ // Enable multi-pass compilation for enhanced performance // in larger projects. Good default. new webpack.HotModuleReplacementPlugin({ multiStep: true }), ], devServer: { // Enable history API fallback so HTML5 History API based // routing works. This is a good default that will come // in handy in more complicated setups. historyApiFallback: true, // Unlike the cli flag, this doesn"t set // HotModuleReplacementPlugin! hot: true, inline: true, // Display only errors to reduce the amount of output. stats: "errors-only", host: "localhost", // Defaults to `localhost` process.env.HOST port: "8080", // Defaults to 8080 process.env.PORT proxy: { "/devApi/*": { target: "http://localhost:8888/", secure: true, /* * rewrite 的方式擴展性更強,不限制服務的名稱 * */ rewrite: function (req) { req.url = req.url.replace(/^/devApi/, ""); } } } } ...
然后配置一個全局的環(huán)境變量,通過DefinePlugin:
// webpack.config.js ... plugins: [ new webpack.DefinePlugin({ __DEVAPI__: devServer ? "/devApi/" : """", }), ] ...
最后在調(diào)用服務的地方,只需要在調(diào)用地址前添加 __DEVAPI__全局環(huán)境變量即可,如:
$.ajax({ url: __DEVAPI__ + "http://localhost:8888/", data: {}, type: "get", dataType: "text", success: function (text) {} })
這樣在熱更新的模式下,當有__DEVAPI__ 的地方就會自動識別為/devApi/,而這里會通過代理處理幫你重寫掉,繞過同源策略。
自動打開瀏覽器雖然以上的工作幾乎已經(jīng)滿足我們對webpack的要求了,但是我們還想懶一點,想在熱更新模式下,編譯完成后自動打開瀏覽器。那么我們可以通過這個插件open-browser-webpack-plugin解決:
引用插件
// webpack.config.js var OpenBrowserPlugin = require("open-browser-webpack-plugin");
配置插件,這個配置要根據(jù)項目的具體情況去配置:
// webpack.config.js ... plugins: [ new OpenBrowserPlugin({url: "http://localhost:8080" + PATHS.publicPath + "index.html"}) ] ...總結
以上就是這篇文章的主要內(nèi)容,希望通過這篇文章能夠給大家?guī)硪恍﹩l(fā)。如果有覺得哪里不對,或者不合理的地方,歡迎指出。其實webpack還有一個關于版本號的bug,不知道是不是有人解決了,如果有人已經(jīng)解決了,還請分享。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79887.html
摘要:本文相關代碼已經(jīng)存放在,可自行下載使用多入口復習的優(yōu)勢不言而喻,因此在實際應用中我們也常常使用它調(diào)試多入口應用,所謂多入口是指多個頁面會使用多個入口文件,在官方教程介紹了如何配置這里指定了個入口文件,打包之后分別會在文件夾中生成個打包之后 本文相關代碼已經(jīng)存放在 dynamic-entry,可自行下載使用 0. 多入口 (復習) webpack 的優(yōu)勢不言而喻,因此在實際應用中我們也常...
摘要:原作者原鏈接基于多入口生成模板用于服務端渲染的方案及實戰(zhàn)法律聲明警告本作品遵循署名非商業(yè)性使用禁止演繹未本地化版本協(xié)議發(fā)布。這是什么背景現(xiàn)代化的前端項目中很多都使用了客戶端渲染的單頁面應用。 原作者:@LinuxerPHL原鏈接:基于 Webpack 4 多入口生成模板用于服務端渲染的方案及實戰(zhàn) 法律聲明 警告:本作品遵循 署名-非商業(yè)性使用-禁止演繹3.0 未本地化版本(CC BY-...
摘要:原作者原博文地址基于多入口生成模板用于服務端渲染的方案及實戰(zhàn)法律聲明警告本作品遵循署名非商業(yè)性使用禁止演繹未本地化版本協(xié)議發(fā)布。這是什么背景現(xiàn)代化的前端項目中很多都使用了客戶端渲染的單頁面應用。 原作者:@LinuxerPHL原博文地址: 基于 Webpack 4 多入口生成模板用于服務端渲染的方案及實戰(zhàn) 法律聲明 警告:本作品遵循 署名-非商業(yè)性使用-禁止演繹3.0 未本地化版本(...
摘要:特性比較熱門的兩大特性,零配置和速度快號稱提速上限一般情況下,相比于低版本,場景下第三方依賴打包速度和場景下本地服務首次啟動速度都得到顯著提升零配置通過指定當前場景為開發(fā)模式還是生產(chǎn)模式,自動設置好當前場景的默認配置,用戶即可馬上使用,不需 webpack4特性 webpack4比較熱門的兩大特性,零配置和速度快(號稱提速上限98%) 一般情況下,webpack4相比于低版本,prod...
摘要:原因就是默認會把最重要的東西放到公共里,這里面包含啟動應用程序的依賴項模塊與模塊的依賴關系以及文件的版本號等信息。 之前寫了一篇關于webpack 如何使用的文章:webpack 單頁面應用實戰(zhàn),并且寫了一個 單頁面應用的小項目 放到了github上。正巧公司前段時間用webpack 做了一個項目,項目不大,是基于單頁面應用的。但是上線后才發(fā)現(xiàn)了一些問題,原來還是有一些要優(yōu)化改進的地方...
閱讀 996·2023-04-25 14:45
閱讀 2772·2021-09-30 09:59
閱讀 3114·2021-09-22 15:48
閱讀 2425·2019-08-30 15:55
閱讀 3467·2019-08-30 15:44
閱讀 535·2019-08-29 14:07
閱讀 3412·2019-08-26 13:45
閱讀 536·2019-08-26 11:31