摘要:和類似的預(yù)處理器還有等。的用處非常多,包括給自動(dòng)加前綴使用下一代語(yǔ)法等,目前越來(lái)越多的人開始用它,它很可能會(huì)成為預(yù)處理器的最終贏家。
webpack實(shí)戰(zhàn)
查看所有文檔頁(yè)面:全棧開發(fā),獲取更多信息。快馬加鞭,加班加點(diǎn),終于把這個(gè)文檔整理出來(lái)了,順便深入地學(xué)習(xí)一番,鞏固知識(shí),就是太累人,影響睡眠時(shí)間和質(zhì)量。極客就是想要把事情做到極致,開始了就必須到達(dá)終點(diǎn)。
原文鏈接:webpack實(shí)戰(zhàn),原文廣告模態(tài)框遮擋,閱讀體驗(yàn)不好,所以整理成本文,方便查找。
本章教你如何用 Webpack 去解決實(shí)際項(xiàng)目中常見的場(chǎng)景。
按照不同場(chǎng)景劃分成以下幾類:
使用新語(yǔ)言來(lái)開發(fā)項(xiàng)目:
使用 ES6 語(yǔ)言
使用 TypeScript 語(yǔ)言
使用 Flow 檢查器
使用 SCSS 語(yǔ)言
使用 PostCSS
使用新框架來(lái)開發(fā)項(xiàng)目:
使用 React 框架
使用 Vue 框架
使用 Angular2 框架
用 Webpack 構(gòu)建單頁(yè)應(yīng)用:
為單頁(yè)應(yīng)用生成 HTML
管理多個(gè)單頁(yè)應(yīng)用
用 Webpack 構(gòu)建不同運(yùn)行環(huán)境的項(xiàng)目:
構(gòu)建同構(gòu)應(yīng)用
構(gòu)建 Electron 應(yīng)用
構(gòu)建 Npm 模塊
構(gòu)建離線應(yīng)用
Webpack 結(jié)合其它工具搭配使用,各取所長(zhǎng):
搭配 Npm Script
檢查代碼
通過(guò) Node.js API 啟動(dòng) Webpack
使用 Webpack Dev Middleware
用 Webpack 加載特殊類型的資源:
加載圖片
加載SVG
加載 Source Map
使用 TypeScript 語(yǔ)言由于本文不推薦使用TypeScript,ES6就足夠完成大部分任務(wù)。原文鏈接:使用 TypeScript 語(yǔ)言
使用 Angular2 框架Angular2不在我的技術(shù)棧范圍,所以這一章不加入,有興趣的查看原文:使用 Angular2 框架
使用ES6語(yǔ)言通常我們需要把采用 ES6 編寫的代碼轉(zhuǎn)換成目前已經(jīng)支持良好的 ES5 代碼,這包含2件事:
把新的 ES6 語(yǔ)法用 ES5 實(shí)現(xiàn),例如 ES6 的 class 語(yǔ)法用 ES5 的 prototype 實(shí)現(xiàn)。
給新的 API 注入 polyfill ,例如使用新的 fetch API 時(shí)注入對(duì)應(yīng)的 polyfill 后才能讓低端瀏覽器正常運(yùn)行。
BabelBabel 可以方便的完成以上2件事。
Babel 是一個(gè) JavaScript 編譯器,能將 ES6 代碼轉(zhuǎn)為 ES5 代碼,讓你使用最新的語(yǔ)言特性而不用擔(dān)心兼容性問(wèn)題,并且可以通過(guò)插件機(jī)制根據(jù)需求靈活的擴(kuò)展。
在 Babel 執(zhí)行編譯的過(guò)程中,會(huì)從項(xiàng)目根目錄下的 .babelrc 文件讀取配置。.babelrc 是一個(gè) JSON 格式的文件,內(nèi)容大致如下:
{ "plugins": [ [ "transform-runtime", { "polyfill": false } ] ], "presets": [ [ "es2015", { "modules": false } ], "stage-2", "react" ] }Plugins
plugins 屬性告訴 Babel 要使用哪些插件,插件可以控制如何轉(zhuǎn)換代碼。
以上配置文件里的 transform-runtime 對(duì)應(yīng)的插件全名叫做 babel-plugin-transform-runtime,即在前面加上了 babel-plugin-,要讓 Babel 正常運(yùn)行我們必須先安裝它:
npm i -D babel-plugin-transform-runtime
babel-plugin-transform-runtime 是 Babel 官方提供的一個(gè)插件,作用是減少冗余代碼。
Babel 在把 ES6 代碼轉(zhuǎn)換成 ES5 代碼時(shí)通常需要一些 ES5 寫的輔助函數(shù)來(lái)完成新語(yǔ)法的實(shí)現(xiàn),例如在轉(zhuǎn)換 class extent 語(yǔ)法時(shí)會(huì)在轉(zhuǎn)換后的 ES5 代碼里注入 _extent 輔助函數(shù)用于實(shí)現(xiàn)繼承:
function _extent(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }
這會(huì)導(dǎo)致每個(gè)使用了 class extent 語(yǔ)法的文件都被注入重復(fù)的 _extent 輔助函數(shù)代碼,babel-plugin-transform-runtime 的作用在于不把輔助函數(shù)內(nèi)容注入到文件里,而是注入一條導(dǎo)入語(yǔ)句:
var _extent = require("babel-runtime/helpers/_extent");
這樣能減小 Babel 編譯出來(lái)的代碼的文件大小。
同時(shí)需要注意的是由于 babel-plugin-transform-runtime 注入了 require("babel-runtime/helpers/_extent") 語(yǔ)句到編譯后的代碼里,需要安裝 babel-runtime 依賴到你的項(xiàng)目后,代碼才能正常運(yùn)行。 也就是說(shuō) babel-plugin-transform-runtime 和 babel-runtime 需要配套使用,使用了 babel-plugin-transform-runtime 后一定需要 babel-runtime。
Presetspresets 屬性告訴 Babel 要轉(zhuǎn)換的源碼使用了哪些新的語(yǔ)法特性,一個(gè) Presets 對(duì)一組新語(yǔ)法特性提供支持,多個(gè) Presets 可以疊加。
Presets 其實(shí)是一組 Plugins 的集合,每一個(gè) Plugin 完成一個(gè)新語(yǔ)法的轉(zhuǎn)換工作。Presets 是按照 ECMAScript 草案來(lái)組織的,通常可以分為以下三大類:
已經(jīng)被寫入 ECMAScript 標(biāo)準(zhǔn)里的特性,由于之前每年都有新特性被加入到標(biāo)準(zhǔn)里;
env 包含當(dāng)前所有 ECMAScript 標(biāo)準(zhǔn)里的最新特性。
被社區(qū)提出來(lái)的但還未被寫入 ECMAScript 標(biāo)準(zhǔn)里特性,這其中又分為以下四種:
stage0 只是一個(gè)美好激進(jìn)的想法,有 Babel 插件實(shí)現(xiàn)了對(duì)這些特性的支持,但是不確定是否會(huì)被定為標(biāo)準(zhǔn);
stage1 值得被納入標(biāo)準(zhǔn)的特性;
stage2 該特性規(guī)范已經(jīng)被起草,將會(huì)被納入標(biāo)準(zhǔn)里;
stage3 該特性規(guī)范已經(jīng)定稿,各大瀏覽器廠商和 Node.js 社區(qū)開始著手實(shí)現(xiàn);
stage4 在接下來(lái)的一年將會(huì)加入到標(biāo)準(zhǔn)里去。
為了支持一些特定應(yīng)用場(chǎng)景下的語(yǔ)法,和 ECMAScript 標(biāo)準(zhǔn)沒(méi)有關(guān)系,例如 babel-preset-react 是為了支持 React 開發(fā)中的 JSX 語(yǔ)法。
在實(shí)際應(yīng)用中,你需要根據(jù)項(xiàng)目源碼所使用的語(yǔ)法去安裝對(duì)應(yīng)的 Plugins 或 Presets。
接入 Babel由于 Babel 所做的事情是轉(zhuǎn)換代碼,所以應(yīng)該通過(guò) Loader 去接入 Babel,Webpack 配置如下:
module.exports = { module: { rules: [ { test: /.js$/, use: ["babel-loader"], }, ] }, // 輸出 source-map 方便直接調(diào)試 ES6 源碼 devtool: "source-map" };
配置命中了項(xiàng)目目錄下所有的 JavaScript 文件,通過(guò) babel-loader 去調(diào)用 Babel 完成轉(zhuǎn)換工作。 在重新執(zhí)行構(gòu)建前,需要先安裝新引入的依賴:
# Webpack 接入 Babel 必須依賴的模塊 npm i -D babel-core babel-loader # 根據(jù)你的需求選擇不同的 Plugins 或 Presets npm i -D babel-preset-env使用SCSS語(yǔ)言
SCSS 可以讓你用更靈活的方式寫 CSS。 它是一種 CSS 預(yù)處理器,語(yǔ)法和 CSS 相似,但加入了變量、邏輯等編程元素,代碼類似這樣:
$blue: #1875e7; div { color: $blue; }
SCSS 又叫 SASS,區(qū)別在于 SASS 語(yǔ)法類似 Ruby,而 SCSS 語(yǔ)法類似 CSS,對(duì)于熟悉 CSS 的前端工程師來(lái)說(shuō)會(huì)更喜歡 SCSS。
采用 SCSS 去寫 CSS 的好處在于可以方便地管理代碼,抽離公共的部分,通過(guò)邏輯寫出更靈活的代碼。 和 SCSS 類似的 CSS 預(yù)處理器還有 LESS 等。
使用 SCSS 可以提升編碼效率,但是必須把 SCSS 源代碼編譯成可以直接在瀏覽器環(huán)境下運(yùn)行的 CSS 代碼。
node-sass 核心模塊是由 C++ 編寫,再用 Node.js 封裝了一層,以供給其它 Node.js 調(diào)用。 node-sass 還支持通過(guò)命令行調(diào)用,先安裝它到全局:
npm i -g node-sass
再執(zhí)行編譯命令:
# 把 main.scss 源文件編譯成 main.css node-sass main.scss main.css
你就能在源碼同目錄下看到編譯后的 main.css 文件。
接入 WebpackWebpack 接入 sass-loader 相關(guān)配置如下:
module.exports = { module: { rules: [ { // 增加對(duì) SCSS 文件的支持 test: /.scss/, // SCSS 文件的處理順序?yàn)橄?sass-loader 再 css-loader 再 style-loader use: ["style-loader", "css-loader", "sass-loader"], }, ] }, };
以上配置通過(guò)正則 /.scss/ 匹配所有以 .scss 為后綴的 SCSS 文件,再分別使用3個(gè) Loader 去處理。具體處理流程如下:
通過(guò) sass-loader 把 SCSS 源碼轉(zhuǎn)換為 CSS 代碼,再把 CSS 代碼交給 css-loader 去處理。
css-loader 會(huì)找出 CSS 代碼中的 @import 和 url() 這樣的導(dǎo)入語(yǔ)句,告訴 Webpack 依賴這些資源。同時(shí)還支持 CSS Modules、壓縮 CSS 等功能。處理完后再把結(jié)果交給 style-loader 去處理。
style-loader 會(huì)把 CSS 代碼轉(zhuǎn)換成字符串后,注入到 JavaScript 代碼中去,通過(guò) JavaScript 去給 DOM 增加樣式。如果你想把 CSS 代碼提取到一個(gè)多帶帶的文件而不是和 JavaScript 混在一起,可以使用1-5 使用Plugin 中介紹過(guò)的 ExtractTextPlugin。
由于接入 sass-loader,項(xiàng)目需要安裝這些新的依賴:
# 安裝 Webpack Loader 依賴 npm i -D sass-loader css-loader style-loader # sass-loader 依賴 node-sass npm i -D node-sass使用Flow檢查器
Flow 是一個(gè) Facebook 開源的 JavaScript 靜態(tài)類型檢測(cè)器,它是 JavaScript 語(yǔ)言的超集。
你所需要做的就是在需要的地方加上類型檢查,例如在兩個(gè)由不同人開發(fā)的模塊對(duì)接的接口出加上靜態(tài)類型檢查,能在編譯階段就指出部分模塊使用不當(dāng)?shù)膯?wèn)題。 同時(shí) Flow 也能通過(guò)類型推斷檢查出 JavaScript 代碼中潛在的 Bug。
Flow 使用效果如下:
// @flow // 靜態(tài)類型檢查 function square1(n: number): number { return n * n; } square1("2"); // Error: square1 需要傳入 number 作為參數(shù) // 類型推斷檢查 function square2(n) { return n * n; // Error: 傳入的 string 類型不能做乘法運(yùn)算 } square2("2");
需要注意的時(shí)代碼中的第一行 // @flow 告訴 Flow 檢查器這個(gè)文件需要被檢查。使用 Flow
Flow 檢測(cè)器由高性能跨平臺(tái)的 OCaml 語(yǔ)言編寫,它的可執(zhí)行文件可以通過(guò):
npm i -D flow-bin
安裝,安裝完成后通過(guò)先配置 Npm Script:
"scripts": { "flow": "flow" }
再通過(guò) npm run flow 去調(diào)用 Flow 執(zhí)行代碼檢查。
除此之外你還可以通過(guò):
npm i -g flow-bin
把 Flow 安裝到全局后,再直接通過(guò) flow 命令去執(zhí)行代碼檢查。
安裝成功后,在項(xiàng)目根目錄下執(zhí)行 Flow 后,F(xiàn)low 會(huì)遍歷出所有需要檢查的文件并對(duì)其進(jìn)行檢查,輸出錯(cuò)誤結(jié)果到控制臺(tái)。
采用了 Flow 靜態(tài)類型語(yǔ)法的 JavaScript 是無(wú)法直接在目前已有的 JavaScript 引擎中運(yùn)行的,要讓代碼可以運(yùn)行需要把這些靜態(tài)類型語(yǔ)法去掉。
// 采用 Flow 的源代碼 function foo(one: any, two: number, three?): string {} // 去掉靜態(tài)類型語(yǔ)法后輸出代碼 function foo(one, two, three) {}
有兩種方式可以做到這點(diǎn):
flow-remove-types 可多帶帶使用,速度快。
babel-preset-flow 與 Babel 集成。
集成 Webpack由于使用了 Flow 項(xiàng)目一般都會(huì)使用 ES6 語(yǔ)法,所以把 Flow 集成到使用 Webpack 構(gòu)建的項(xiàng)目里最方便的方法是借助 Babel。
安裝 npm i -D babel-preset-flow 依賴到項(xiàng)目。
修改 .babelrc 配置文件,加入 Flow Preset:
"presets": [ ...[], "flow" ]
往源碼里加入靜態(tài)類型后重新構(gòu)建項(xiàng)目,你會(huì)發(fā)現(xiàn)采用了 Flow 的源碼還是能正常在瀏覽器中運(yùn)行。
要明確構(gòu)建的目的只是為了去除源碼中的 Flow 靜態(tài)類型語(yǔ)法,而代碼檢查和構(gòu)建無(wú)關(guān)。 許多編輯器已經(jīng)整合 Flow,可以實(shí)時(shí)在代碼中高亮指出 Flow 檢查出的問(wèn)題。使用PostCSS
PostCSS 是一個(gè) CSS 處理工具,和 SCSS 不同的地方在于它通過(guò)插件機(jī)制可以靈活的擴(kuò)展其支持的特性,而不是像 SCSS 那樣語(yǔ)法是固定的。 PostCSS 的用處非常多,包括給 CSS 自動(dòng)加前綴、使用下一代 CSS 語(yǔ)法等,目前越來(lái)越多的人開始用它,它很可能會(huì)成為 CSS 預(yù)處理器的最終贏家。
PostCSS 和 CSS 的關(guān)系就像 Babel 和 JavaScript 的關(guān)系,它們解除了語(yǔ)法上的禁錮,通過(guò)插件機(jī)制來(lái)擴(kuò)展語(yǔ)言本身,用工程化手段給語(yǔ)言帶來(lái)了更多的可能性。PostCSS 和 SCSS 的關(guān)系就像 Babel 和 TypeScript 的關(guān)系,PostCSS 更加靈活、可擴(kuò)張性強(qiáng),而 SCSS 內(nèi)置了大量功能而不能擴(kuò)展。
給 CSS 自動(dòng)加前綴,增加各瀏覽器的兼容性:
/*輸入*/ h1 { display: flex; } /*輸出*/ h1 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; }
使用下一代 CSS 語(yǔ)法:
/*輸入*/ :root { --red: #d33; } h1 { color: var(--red); } /*輸出*/ h1 { color: #d33; }
PostCSS 全部采用 JavaScript 編寫,運(yùn)行在 Node.js 之上,即提供了給 JavaScript 代碼調(diào)用的模塊,也提供了可執(zhí)行的文件。
在 PostCSS 啟動(dòng)時(shí),會(huì)從目錄下的 postcss.config.js 文件中讀取所需配置,所以需要新建該文件,文件內(nèi)容大致如下:
module.exports = { plugins: [ // 需要使用的插件列表 require("postcss-cssnext") ] }
其中的 postcss-cssnext 插件可以讓你使用下一代 CSS 語(yǔ)法編寫代碼,再通過(guò) PostCSS 轉(zhuǎn)換成目前的瀏覽器可識(shí)別的 CSS,并且該插件還包含給 CSS 自動(dòng)加前綴的功能。
目前 Chrome 等現(xiàn)代瀏覽器已經(jīng)能完全支持 cssnext 中的所有語(yǔ)法,也就是說(shuō)按照 cssnext 語(yǔ)法寫的 CSS 在不經(jīng)過(guò)轉(zhuǎn)換的情況下也能在瀏覽器中直接運(yùn)行。接入 Webpack
雖然使用 PostCSS 后文件后綴還是 .css 但這些文件必須先交給 postcss-loader 處理一遍后再交給 css-loader。
接入 PostCSS 相關(guān)的 Webpack 配置如下:
module.exports = { module: { rules: [ { // 使用 PostCSS 處理 CSS 文件 test: /.css/, use: ["style-loader", "css-loader", "postcss-loader"], }, ] }, };
接入 PostCSS 給項(xiàng)目帶來(lái)了新的依賴需要安裝,如下:
# 安裝 Webpack Loader 依賴 npm i -D postcss-loader css-loader style-loader # 根據(jù)你使用的特性安裝對(duì)應(yīng)的 PostCSS 插件依賴 npm i -D postcss-cssnext使用React框架 React 語(yǔ)法特征
使用了 React 項(xiàng)目的代碼特征有 JSX 和 Class 語(yǔ)法,例如:
class Button extends Component { render() { returnHello,Webpack
} }
在使用了 React 的項(xiàng)目里 JSX 和 Class 語(yǔ)法并不是必須的,但使用新語(yǔ)法寫出的代碼看上去更優(yōu)雅。
其中 JSX 語(yǔ)法是無(wú)法在任何現(xiàn)有的 JavaScript 引擎中運(yùn)行的,所以在構(gòu)建過(guò)程中需要把源碼轉(zhuǎn)換成可以運(yùn)行的代碼,例如:
// 原 JSX 語(yǔ)法代碼 returnReact 與 BabelHello,Webpack
// 被轉(zhuǎn)換成正常的 JavaScript 代碼 return React.createElement("h1", null, "Hello,Webpack")
要在使用 Babel 的項(xiàng)目中接入 React 框架是很簡(jiǎn)單的,只需要加入 React 所依賴的 Presets babel-preset-react。
通過(guò)以下命令:
# 安裝 React 基礎(chǔ)依賴 npm i -D react react-dom # 安裝 babel 完成語(yǔ)法轉(zhuǎn)換所需依賴 npm i -D babel-preset-react
安裝新的依賴后,再修改 .babelrc 配置文件加入 React Presets
"presets": [ "react" ],
就完成了一切準(zhǔn)備工作。
再修改 main.js 文件如下:
import * as React from "react"; import { Component } from "react"; import { render } from "react-dom"; class Button extends Component { render() { returnHello,Webpack
} } render(, window.document.getElementById("app"));
重新執(zhí)行構(gòu)建打開網(wǎng)頁(yè)你將會(huì)發(fā)現(xiàn)由 React 渲染出來(lái)的 Hello,Webpack。
React 與 TypeScriptTypeScript 相比于 Babel 的優(yōu)點(diǎn)在于它原生支持 JSX 語(yǔ)法,你不需要重新安裝新的依賴,只需修改一行配置。 但 TypeScript 的不同在于:
使用了 JSX 語(yǔ)法的文件后綴必須是 tsx。
由于 React 不是采用 TypeScript 編寫的,需要安裝 react 和 react-dom 對(duì)應(yīng)的 TypeScript 接口描述模塊 @types/react 和 @types/react-dom 后才能通過(guò)編譯。
修改 TypeScript 編譯器配置文件 tsconfig.json 增加對(duì) JSX 語(yǔ)法的支持,如下:
{ "compilerOptions": { "jsx": "react" // 開啟 jsx ,支持 React } }
由于 main.js 文件中存在 JSX 語(yǔ)法,再把 main.js 文件重命名為 main.tsx,同時(shí)修改文件內(nèi)容為在上面 React 與 Babel 里所采用的 React 代碼。 同時(shí)為了讓 Webpack 對(duì)項(xiàng)目里的 ts 與 tsx 原文件都采用 awesome-typescript-loader 去轉(zhuǎn)換, 需要注意的是 Webpack Loader 配置的 test 選項(xiàng)需要匹配到 tsx 類型的文件,并且 extensions 中也要加上 .tsx,配置如下:
module.exports = { // TS 執(zhí)行入口文件 entry: "./main", output: { filename: "bundle.js", path: path.resolve(__dirname, "./dist"), }, resolve: { // 先嘗試 ts,tsx 后綴的 TypeScript 源碼文件 extensions: [".ts", ".tsx", ".js",] }, module: { rules: [ { // 同時(shí)匹配 ts,tsx 后綴的 TypeScript 源碼文件 test: /.tsx?$/, loader: "awesome-typescript-loader" } ] }, devtool: "source-map",// 輸出 Source Map 方便在瀏覽器里調(diào)試 TypeScript 代碼 };
通過(guò)npm i react react-dom @types/react @types/react-dom安裝新的依賴后重啟構(gòu)建,重新打開網(wǎng)頁(yè)你將會(huì)發(fā)現(xiàn)由 React 渲染出來(lái)的 Hello,Webpack。
使用Vue框架Vue是一個(gè)漸進(jìn)式的 MVVM 框架,相比于 React、Angular 它更靈活輕量。 它不會(huì)強(qiáng)制性地內(nèi)置一些功能和語(yǔ)法,你可以根據(jù)自己的需要一點(diǎn)點(diǎn)地添加功能。 雖然采用 Vue 的項(xiàng)目能用可直接運(yùn)行在瀏覽器環(huán)境里的代碼編寫,但為了方便編碼大多數(shù)項(xiàng)目都會(huì)采用 Vue 官方的單文件組件的寫法去編寫項(xiàng)目。
Vue 的單文件組件通過(guò)一個(gè)類似 HTML 文件的 .vue 文件就能描述清楚一個(gè)組件所需的模版、樣式、邏輯。
main.js 入口文件:
import Vue from "vue" import App from "./App.vue" new Vue({ el: "#app", render: h => h(App) });
入口文件創(chuàng)建一個(gè) Vue 的根實(shí)例,在 ID 為 app 的 DOM 節(jié)點(diǎn)上渲染出上面定義的 App 組件。
接入 Webpack目前最成熟和流行的開發(fā) Vue 項(xiàng)目的方式是采用 ES6 加 Babel 轉(zhuǎn)換,這和基本的采用 ES6 開發(fā)的項(xiàng)目很相似,差別在于要解析 .vue 格式的單文件組件。 好在 Vue 官方提供了對(duì)應(yīng)的 vue-loader 可以非常方便的完成單文件組件的轉(zhuǎn)換。
修改 Webpack 相關(guān)配置如下:
module: { rules: [ { test: /.vue$/, use: ["vue-loader"], }, ] }
安裝新引入的依賴:
# Vue 框架運(yùn)行需要的庫(kù) npm i -S vue # 構(gòu)建所需的依賴 npm i -D vue-loader css-loader vue-template-compiler
在這些依賴中,它們的作用分別是:
vue-loader:解析和轉(zhuǎn)換 .vue 文件,提取出其中的邏輯代碼 script、樣式代碼 style、以及 HTML 模版 template,再分別把它們交給對(duì)應(yīng)的 Loader 去處理。
css-loader:加載由 vue-loader 提取出的 CSS 代碼。
vue-template-compiler:把 vue-loader 提取出的 HTML 模版編譯成對(duì)應(yīng)的可執(zhí)行的 JavaScript 代碼,這和 React 中的 JSX 語(yǔ)法被編譯成 JavaScript 代碼類似。預(yù)先編譯好 HTML 模版相對(duì)于在瀏覽器中再去編譯 HTML 模版的好處在于性能更好。
使用 TypeScript 編寫 Vue 應(yīng)用從 Vue 2.5.0+ 版本開始,提供了對(duì) TypeScript 的良好支持,使用 TypeScript 編寫 Vue 是一個(gè)很好的選擇,因?yàn)?TypeScript 能檢查出一些潛在的錯(cuò)誤。
新增 tsconfig.json 配置文件,內(nèi)容如下:
{ "compilerOptions": { // 構(gòu)建出 ES5 版本的 JavaScript,與 Vue 的瀏覽器支持保持一致 "target": "es5", // 開啟嚴(yán)格模式,這可以對(duì) `this` 上的數(shù)據(jù)屬性進(jìn)行更嚴(yán)格的推斷 "strict": true, // TypeScript 編譯器輸出的 JavaScript 采用 es2015 模塊化,使 Tree Shaking 生效 "module": "es2015", "moduleResolution": "node" } }
修改 App.vue 腳本部分內(nèi)容如下:
注意 script 標(biāo)簽中的 lang="ts" 是為了指明代碼的語(yǔ)法是 TypeScript。
修改 main.ts 執(zhí)行入口文件為如下:
import Vue from "vue" import App from "./App.vue" new Vue({ el: "#app", render: h => h(App) });
由于 TypeScript 不認(rèn)識(shí) .vue 結(jié)尾的文件,為了讓其支持 import App from "./App.vue" 導(dǎo)入語(yǔ)句,還需要以下文件 vue-shims.d.ts 去定義 .vue 的類型:
// 告訴 TypeScript 編譯器 .vue 文件其實(shí)是一個(gè) Vue declare module "*.vue" { import Vue from "vue"; export default Vue; }
Webpack 配置需要修改兩個(gè)地方,如下:
const path = require("path"); module.exports = { resolve: { // 增加對(duì) TypeScript 的 .ts 和 .vue 文件的支持 extensions: [".ts", ".js", ".vue", ".json"], }, module: { rules: [ // 加載 .ts 文件 { test: /.ts$/, loader: "ts-loader", exclude: /node_modules/, options: { // 讓 tsc 把 vue 文件當(dāng)成一個(gè) TypeScript 模塊去處理,以解決 moudle not found 的問(wèn)題,tsc 本身不會(huì)處理 .vue 結(jié)尾的文件 appendTsSuffixTo: [/.vue$/], } }, ] }, };
除此之外還需要安裝新引入的依賴:npm i -D ts-loader typescript
為單頁(yè)應(yīng)用生成HTML 引入問(wèn)題在使用 React 框架中,是用最簡(jiǎn)單的 Hello,Webpack 作為例子讓大家理解, 這個(gè)例子里因?yàn)橹惠敵隽艘粋€(gè) bundle.js 文件,所以手寫了一個(gè) index.html 文件去引入這個(gè) bundle.js,才能讓應(yīng)用在瀏覽器中運(yùn)行起來(lái)。
在實(shí)際項(xiàng)目中遠(yuǎn)比這復(fù)雜,一個(gè)頁(yè)面常常有很多資源要加載。接下來(lái)舉一個(gè)實(shí)戰(zhàn)中的例子,要求如下:
項(xiàng)目采用 ES6 語(yǔ)言加 React 框架。
給頁(yè)面加入 Google Analytics,這部分代碼需要內(nèi)嵌進(jìn) HEAD 標(biāo)簽里去。
給頁(yè)面加入 Disqus 用戶評(píng)論,這部分代碼需要異步加載以提升首屏加載速度。
壓縮和分離 JavaScript 和 CSS 代碼,提升加載速度。
在開始前先來(lái)看看該應(yīng)用最終發(fā)布到線上的代碼。
可以看到部分代碼被內(nèi)嵌進(jìn)了 HTML 的 HEAD 標(biāo)簽中,部分文件的文件名稱被打上根據(jù)文件內(nèi)容算出的 Hash 值,并且加載這些文件的 URL 地址也被正常的注入到了 HTML 中。
解決方案推薦一個(gè)用于方便地解決以上問(wèn)題的 Webpack 插件 web-webpack-plugin。 該插件已經(jīng)被社區(qū)上許多人使用和驗(yàn)證,解決了大家的痛點(diǎn)獲得了很多好評(píng),下面具體介紹如何用它來(lái)解決上面的問(wèn)題。
首先,修改 Webpack 配置。
以上配置中,大多數(shù)都是按照前面已經(jīng)講過(guò)的內(nèi)容增加的配置,例如:
增加對(duì) CSS 文件的支持,提取出 Chunk 中的 CSS 代碼到多帶帶的文件中,壓縮 CSS 文件;
定義 NODE_ENV 環(huán)境變量為 production,以去除源碼中只有開發(fā)時(shí)才需要的部分;
給輸出的文件名稱加上 Hash 值;
壓縮輸出的 JavaScript 代碼。
但最核心的部分在于 plugins 里的:
new WebPlugin({ template: "./template.html", // HTML 模版文件所在的文件路徑 filename: "index.html" // 輸出的 HTML 的文件名稱 })
其中 template: "./template.html" 所指的模版文件 template.html 的內(nèi)容是:
該文件描述了哪些資源需要被以何種方式加入到輸出的 HTML 文件中。
以 為例,按照正常引入 CSS 文件一樣的語(yǔ)法來(lái)引入 Webpack 生產(chǎn)的代碼。href 屬性中的 app?_inline 可以分為兩部分,前面的 app 表示 CSS 代碼來(lái)自名叫 app 的 Chunk 中,后面的 _inline 表示這些代碼需要被內(nèi)嵌到這個(gè)標(biāo)簽所在的位置。
同樣的 表示 JavaScript 代碼來(lái)自相對(duì)于當(dāng)前模版文件 template.html 的本地文件 ./google_analytics.js, 而且文件中的 JavaScript 代碼也需要被內(nèi)嵌到這個(gè)標(biāo)簽所在的位置。
也就是說(shuō)資源鏈接 URL 字符串里問(wèn)號(hào)前面的部分表示資源內(nèi)容來(lái)自哪里,后面的 querystring 表示這些資源注入的方式。
除了 _inline 表示內(nèi)嵌外,還支持以下屬性:
_dist 只有在生產(chǎn)環(huán)境下才引入該資源;
_dev 只有在開發(fā)環(huán)境下才引入該資源;
_ie 只有IE瀏覽器才需要引入的資源,通過(guò) [if IE]>resource 注釋實(shí)現(xiàn)。
這些屬性之間可以搭配使用,互不沖突。例如 app?_inline&_dist 表示只在生產(chǎn)環(huán)境下才引入該資源,并且需要內(nèi)嵌到 HTML 里去。
WebPlugin 插件還支持一些其它更高級(jí)的用法,詳情可以訪問(wèn)該項(xiàng)目主頁(yè)閱讀文檔。
管理多個(gè)單頁(yè)應(yīng)用 引入問(wèn)題在開始前先來(lái)看看該應(yīng)用最終發(fā)布到線上的代碼。
構(gòu)建出的目錄結(jié)構(gòu)為:
dist ├── common_029086ff.js ├── common_7cc98ad0.css ├── index.html ├── index_04c08fbf.css ├── index_b3d3761c.js ├── login.html ├── login_0a3feca9.js └── login_e31e214b.css
如果按照上節(jié)的思路,可能需要為每個(gè)單頁(yè)應(yīng)用配置一段如下代碼:
new WebPlugin({ template: "./template.html", // HTML 模版文件所在的文件路徑 filename: "login.html" // 輸出的 HTML 的文件名稱 })
并且把頁(yè)面對(duì)應(yīng)的入口加入到 enrty 配置項(xiàng)中,就像這樣:
entry: { index: "./pages/index/index.js",// 頁(yè)面 index.html 的入口文件 login: "./pages/login/index.js",// 頁(yè)面 login.html 的入口文件 }
當(dāng)有新頁(yè)面加入時(shí)就需要修改 Webpack 配置文件,新插入一段以上代碼,這會(huì)導(dǎo)致構(gòu)建代碼難以維護(hù)而且易錯(cuò)。
解決方案項(xiàng)目源碼目錄結(jié)構(gòu)如下:
├── pages │ ├── index │ │ ├── index.css // 該頁(yè)面多帶帶需要的 CSS 樣式 │ │ └── index.js // 該頁(yè)面的入口文件 │ └── login │ ├── index.css │ └── index.js ├── common.css // 所有頁(yè)面都需要的公共 CSS 樣式 ├── google_analytics.js ├── template.html └── webpack.config.js
從目錄結(jié)構(gòu)中可以看成出下幾點(diǎn)要求:
所有單頁(yè)應(yīng)用的代碼都需要放到一個(gè)目錄下,例如都放在 pages 目錄下;
一個(gè)單頁(yè)應(yīng)用一個(gè)多帶帶的文件夾,例如最后生成的 index.html 相關(guān)的代碼都在 index 目錄下,login.html 同理;
每個(gè)單頁(yè)應(yīng)用的目錄下都有一個(gè) index.js 文件作為入口執(zhí)行文件。
雖然 AutoWebPlugin 強(qiáng)制性的規(guī)定了項(xiàng)目部分的目錄結(jié)構(gòu),但從實(shí)戰(zhàn)經(jīng)驗(yàn)來(lái)看這是一種優(yōu)雅的目錄規(guī)范,合理的拆分了代碼,又能讓新人快速的看懂項(xiàng)目結(jié)構(gòu),也方便日后的維護(hù)。
Webpack 配置文件修改如下:
See the Pen webpack管理多個(gè)單頁(yè)應(yīng)用 by whjin (@whjin) on CodePen.
AutoWebPlugin 會(huì)找出 pages 目錄下的2個(gè)文件夾 index 和 login,把這兩個(gè)文件夾看成兩個(gè)單頁(yè)應(yīng)用。 并且分別為每個(gè)單頁(yè)應(yīng)用生成一個(gè) Chunk 配置和 WebPlugin 配置。 每個(gè)單頁(yè)應(yīng)用的 Chunk 名稱就等于文件夾的名稱,也就是說(shuō) autoWebPlugin.entry() 方法返回的內(nèi)容其實(shí)是:
{ "index":["./pages/index/index.js","./common.css"], "login":["./pages/login/index.js","./common.css"] }
但這些事情 AutoWebPlugin 都會(huì)自動(dòng)為你完成,你不用操心,明白大致原理即可。
template.html 模版文件如下:
由于這個(gè)模版文件被當(dāng)作項(xiàng)目中所有單頁(yè)應(yīng)用的模版,就不能再像上一節(jié)中直接寫 Chunk 的名稱去引入資源,因?yàn)樾枰蛔⑷氲疆?dāng)前頁(yè)面的 Chunk 名稱是不定的,每個(gè)單頁(yè)應(yīng)用都會(huì)有自己的名稱。 和 的作用在于保證該頁(yè)面所依賴的資源都會(huì)被注入到生成的 HTML 模版里去。
web-webpack-plugin 能分析出每個(gè)頁(yè)面依賴哪些資源,例如對(duì)于 login.html 來(lái)說(shuō),插件可以確定該頁(yè)面依賴以下資源:
所有頁(yè)面都依賴的公共 CSS 代碼 common.css;
所有頁(yè)面都依賴的公共 JavaScrip 代碼 common.js;
只有這個(gè)頁(yè)面依賴的 CSS 代碼 login.css;
只有這個(gè)頁(yè)面依賴的 JavaScrip 代碼 login.css。
由于模版文件 template.html 里沒(méi)有指出引入這些依賴資源的 HTML 語(yǔ)句,插件會(huì)自動(dòng)將沒(méi)有手動(dòng)導(dǎo)入但頁(yè)面依賴的資源按照不同類型注入到 和 所在的位置。
CSS 類型的文件注入到 所在的位置,如果 不存在就注入到 HTML HEAD 標(biāo)簽的最后;
JavaScrip 類型的文件注入到 所在的位置,如果 不存在就注入到 HTML BODY 標(biāo)簽的最后。
如果后續(xù)有新的頁(yè)面需要開發(fā),只需要在 pages 目錄下新建一個(gè)目錄,目錄名稱取為輸出 HTML 文件的名稱,目錄下放這個(gè)頁(yè)面相關(guān)的代碼即可,無(wú)需改動(dòng)構(gòu)建代碼。
由于 AutoWebPlugin 是間接的通過(guò)上一節(jié)提到的 WebPlugin 實(shí)現(xiàn)的,WebPlugin 支持的功能 AutoWebPlugin 都支持。
構(gòu)建同構(gòu)應(yīng)用同構(gòu)應(yīng)用是指寫一份代碼但可同時(shí)在瀏覽器和服務(wù)器中運(yùn)行的應(yīng)用。
認(rèn)識(shí)同構(gòu)應(yīng)用現(xiàn)在大多數(shù)單頁(yè)應(yīng)用的視圖都是通過(guò) JavaScript 代碼在瀏覽器端渲染出來(lái)的,但在瀏覽器端渲染的壞處有:
搜索引擎無(wú)法收錄你的網(wǎng)頁(yè),因?yàn)檎故境龅臄?shù)據(jù)都是在瀏覽器端異步渲染出來(lái)的,大部分爬蟲無(wú)法獲取到這些數(shù)據(jù)。
對(duì)于復(fù)雜的單頁(yè)應(yīng)用,渲染過(guò)程計(jì)算量大,對(duì)低端移動(dòng)設(shè)備來(lái)說(shuō)可能會(huì)有性能問(wèn)題,用戶能明顯感知到首屏的渲染延遲。
為了解決以上問(wèn)題,有人提出能否將原本只運(yùn)行在瀏覽器中的 JavaScript 渲染代碼也在服務(wù)器端運(yùn)行,在服務(wù)器端渲染出帶內(nèi)容的 HTML 后再返回。 這樣就能讓搜索引擎爬蟲直接抓取到帶數(shù)據(jù)的 HTML,同時(shí)也能降低首屏渲染時(shí)間。 由于 Node.js 的流行和成熟,以及虛擬 DOM 提出與實(shí)現(xiàn),使這個(gè)假設(shè)成為可能。
實(shí)際上現(xiàn)在主流的前端框架都支持同構(gòu),包括 React、Vue2、Angular2,其中最先支持也是最成熟的同構(gòu)方案是 React。 由于 React 使用者更多,它們之間又很相似,本節(jié)只介紹如何用 Webpack 構(gòu)建 React 同構(gòu)應(yīng)用。
同構(gòu)應(yīng)用運(yùn)行原理的核心在于虛擬 DOM,虛擬 DOM 的意思是不直接操作 DOM 而是通過(guò) JavaScript Object 去描述原本的 DOM 結(jié)構(gòu)。 在需要更新 DOM 時(shí)不直接操作 DOM 樹,而是通過(guò)更新 JavaScript Object 后再映射成 DOM 操作。
虛擬 DOM 的優(yōu)點(diǎn)在于:
因?yàn)椴僮?DOM 樹是高耗時(shí)的操作,盡量減少 DOM 樹操作能優(yōu)化網(wǎng)頁(yè)性能。而 DOM Diff 算法能找出2個(gè)不同 Object 的最小差異,得出最小 DOM 操作;
虛擬 DOM 的在渲染的時(shí)候不僅僅可以通過(guò)操作 DOM 樹來(lái)表示出結(jié)果,也能有其它的表示方式,例如把虛擬 DOM 渲染成字符串(服務(wù)器端渲染),或者渲染成手機(jī) App 原生的 UI 組件( React Native)。
以 React 為例,核心模塊 react 負(fù)責(zé)管理 React 組件的生命周期,而具體的渲染工作可以交給 react-dom 模塊來(lái)負(fù)責(zé)。
react-dom 在渲染虛擬 DOM 樹時(shí)有2中方式可選:
通過(guò) render() 函數(shù)去操作瀏覽器 DOM 樹來(lái)展示出結(jié)果。
通過(guò) renderToString() 計(jì)算出表示虛擬 DOM 的 HTML 形式的字符串。
構(gòu)建同構(gòu)應(yīng)用的最終目的是從一份項(xiàng)目源碼中構(gòu)建出2份 JavaScript 代碼,一份用于在瀏覽器端運(yùn)行,一份用于在 Node.js 環(huán)境中運(yùn)行渲染出 HTML。 其中用于在 Node.js 環(huán)境中運(yùn)行的 JavaScript 代碼需要注意以下幾點(diǎn):
不能包含瀏覽器環(huán)境提供的 API,例如使用 document 進(jìn)行 DOM 操作,因?yàn)?Node.js 不支持這些 API;
不能包含 CSS 代碼,因?yàn)榉?wù)端渲染的目的是渲染出 HTML 內(nèi)容,渲染出 CSS 代碼會(huì)增加額外的計(jì)算量,影響服務(wù)端渲染性能;
不能像用于瀏覽器環(huán)境的輸出代碼那樣把 node_modules 里的第三方模塊和 Node.js 原生模塊(例如 fs 模塊)打包進(jìn)去,而是需要通過(guò) CommonJS 規(guī)范去引入這些模塊。
需要通過(guò) CommonJS 規(guī)范導(dǎo)出一個(gè)渲染函數(shù),以用于在 HTTP 服務(wù)器中去執(zhí)行這個(gè)渲染函數(shù),渲染出 HTML 內(nèi)容返回。
解決方案用于構(gòu)建瀏覽器環(huán)境代碼的 webpack.config.js 配置文件保留不變,新建一個(gè)專門用于構(gòu)建服務(wù)端渲染代碼的配置文件 webpack_server.config.js,內(nèi)容如下:
const path = require("path"); const nodeExternals = require("webpack-node-externals"); module.exports = { // JS 執(zhí)行入口文件 entry: "./main_server.js", // 為了不把 Node.js 內(nèi)置的模塊打包進(jìn)輸出文件中,例如 fs net 模塊等 target: "node", // 為了不把 node_modules 目錄下的第三方模塊打包進(jìn)輸出文件中 externals: [nodeExternals()], output: { // 為了以 CommonJS2 規(guī)范導(dǎo)出渲染函數(shù),以給采用 Node.js 編寫的 HTTP 服務(wù)調(diào)用 libraryTarget: "commonjs2", // 把最終可在 Node.js 中運(yùn)行的代碼輸出到一個(gè) bundle_server.js 文件 filename: "bundle_server.js", // 輸出文件都放到 dist 目錄下 path: path.resolve(__dirname, "./dist"), }, module: { rules: [ { test: /.js$/, use: ["babel-loader"], exclude: path.resolve(__dirname, "node_modules"), }, { // CSS 代碼不能被打包進(jìn)用于服務(wù)端的代碼中去,忽略掉 CSS 文件 test: /.css/, use: ["ignore-loader"], }, ] }, devtool: "source-map" // 輸出 source-map 方便直接調(diào)試 ES6 源碼 };
以上代碼有幾個(gè)關(guān)鍵的地方,分別是:
target: "node" 由于輸出代碼的運(yùn)行環(huán)境是 Node.js,源碼中依賴的 Node.js 原生模塊沒(méi)必要打包進(jìn)去;
externals: [nodeExternals()] webpack-node-externals 的目的是為了防止 node_modules 目錄下的第三方模塊被打包進(jìn)去,因?yàn)?Node.js 默認(rèn)會(huì)去 node_modules 目錄下尋找和使用第三方模塊;
{test: /.css/, use: ["ignore-loader"]} 忽略掉依賴的 CSS 文件,CSS 會(huì)影響服務(wù)端渲染性能,又是做服務(wù)端渲不重要的部分;
libraryTarget: "commonjs2" 以 CommonJS2 規(guī)范導(dǎo)出渲染函數(shù),以供給采用 Node.js 編寫的 HTTP 服務(wù)器代碼調(diào)用。
為了最大限度的復(fù)用代碼,需要調(diào)整下目錄結(jié)構(gòu):
把頁(yè)面的根組件放到一個(gè)多帶帶的文件 AppComponent.js,該文件只能包含根組件的代碼,不能包含渲染入口的代碼,而且需要導(dǎo)出根組件以供給渲染入口調(diào)用,AppComponent.js 內(nèi)容如下:
import React, { Component } from "react"; import "./main.css"; export class AppComponent extends Component { render() { returnHello,Webpack
} }
分別為不同環(huán)境的渲染入口寫兩份不同的文件,分別是用于瀏覽器端渲染 DOM 的 main_browser.js 文件,和用于服務(wù)端渲染 HTML 字符串的 main_server.js 文件。
main_browser.js 文件內(nèi)容如下:
import React from "react"; import { render } from "react-dom"; import { AppComponent } from "./AppComponent"; // 把根組件渲染到 DOM 樹上 render(, window.document.getElementById("app"));
main_server.js 文件內(nèi)容如下:
為了能把渲染的完整 HTML 文件通過(guò) HTTP 服務(wù)返回給請(qǐng)求端,還需要通過(guò)用 Node.js 編寫一個(gè) HTTP 服務(wù)器。 由于本節(jié)不專注于將 HTTP 服務(wù)器的實(shí)現(xiàn),就采用了 ExpressJS 來(lái)實(shí)現(xiàn),http_server.js 文件內(nèi)容如下:
const express = require("express"); const { render } = require("./dist/bundle_server"); const app = express(); // 調(diào)用構(gòu)建出的 bundle_server.js 中暴露出的渲染函數(shù),再拼接下 HTML 模版,形成完整的 HTML 文件 app.get("/", function (req, res) { res.send(`${render()}`); }); // 其它請(qǐng)求路徑返回對(duì)應(yīng)的本地文件 app.use(express.static(".")); app.listen(3000, function () { console.log("app listening on port 3000!") });
再安裝新引入的第三方依賴:
# 安裝 Webpack 構(gòu)建依賴 npm i -D css-loader style-loader ignore-loader webpack-node-externals # 安裝 HTTP 服務(wù)器依賴 npm i -S express
以上所有準(zhǔn)備工作已經(jīng)完成,接下來(lái)執(zhí)行構(gòu)建,編譯出目標(biāo)文件:
執(zhí)行命令 webpack --config webpack_server.config.js 構(gòu)建出用于服務(wù)端渲染的 ./dist/bundle_server.js 文件。
執(zhí)行命令 webpack 構(gòu)建出用于瀏覽器環(huán)境運(yùn)行的 ./dist/bundle_browser.js 文件,默認(rèn)的配置文件為 webpack.config.js。
構(gòu)建執(zhí)行完成后,執(zhí)行 node ./http_server.js 啟動(dòng) HTTP 服務(wù)器后,再用瀏覽器去訪問(wèn) http://localhost:3000 就能看到 Hello,Webpack 了。 但是為了驗(yàn)證服務(wù)端渲染的結(jié)果,你需要打開瀏覽器的開發(fā)工具中的網(wǎng)絡(luò)抓包一欄,再重新刷新瀏覽器后,就能抓到請(qǐng)求 HTML 的包了,抓包效果圖如下:
可以看到服務(wù)器返回的是渲染出內(nèi)容后的 HTML 而不是 HTML 模版,這說(shuō)明同構(gòu)應(yīng)用的改造完成。
本實(shí)例提供項(xiàng)目完整代碼構(gòu)建Electron應(yīng)用
Electron 是 Node.js 和 Chromium 瀏覽器的結(jié)合體,用 Chromium 瀏覽器顯示出的 Web 頁(yè)面作為應(yīng)用的 GUI,通過(guò) Node.js 去和操作系統(tǒng)交互。 當(dāng)你在 Electron 應(yīng)用中的一個(gè)窗口操作時(shí),實(shí)際上是在操作一個(gè)網(wǎng)頁(yè)。當(dāng)你的操作需要通過(guò)操作系統(tǒng)去完成時(shí),網(wǎng)頁(yè)會(huì)通過(guò) Node.js 去和操作系統(tǒng)交互。
采用這種方式開發(fā)桌面端應(yīng)用的優(yōu)點(diǎn)有:
降低開發(fā)門檻,只需掌握網(wǎng)頁(yè)開發(fā)技術(shù)和 Node.js 即可,大量的 Web 開發(fā)技術(shù)和現(xiàn)成庫(kù)可以復(fù)用于 Electron;
由于 Chromium 瀏覽器和 Node.js 都是跨平臺(tái)的,Electron 能做到寫一份代碼在不同的操作系統(tǒng)運(yùn)行。
在運(yùn)行 Electron 應(yīng)用時(shí),會(huì)從啟動(dòng)一個(gè)主進(jìn)程開始。主進(jìn)程的啟動(dòng)是通過(guò) Node.js 去執(zhí)行一個(gè)入口 JavaScript 文件實(shí)現(xiàn)的,這個(gè)入口文件 main.js 內(nèi)容如下:
See the Pen Electron-main.js by whjin (@whjin) on CodePen.
主進(jìn)程啟動(dòng)后會(huì)一直駐留在后臺(tái)運(yùn)行,你眼睛所看得的和操作的窗口并不是主進(jìn)程,而是由主進(jìn)程新啟動(dòng)的窗口子進(jìn)程。
應(yīng)用從啟動(dòng)到退出有一系列生命周期事件,通過(guò) electron.app.on() 函數(shù)去監(jiān)聽生命周期事件,在特定的時(shí)刻做出反應(yīng)。 例如在 app.on("ready") 事件中通過(guò) BrowserWindow 去展示應(yīng)用的主窗口。
啟動(dòng)的窗口其實(shí)是一個(gè)網(wǎng)頁(yè),啟動(dòng)時(shí)會(huì)去加載在 loadURL 中傳入的網(wǎng)頁(yè)地址。 每個(gè)窗口都是一個(gè)多帶帶的網(wǎng)頁(yè)進(jìn)程,窗口之間的通信需要借助主進(jìn)程傳遞消息。
總體來(lái)說(shuō)開發(fā) Electron 應(yīng)用和開發(fā) Web 應(yīng)用很相似,區(qū)別在于 Electron 的運(yùn)行環(huán)境同時(shí)內(nèi)置了瀏覽器和 Node.js 的 API,在開發(fā)網(wǎng)頁(yè)時(shí)除了可以使用瀏覽器提供的 API 外,還可以使用 Node.js 提供的 API。
接入 Webpack接下來(lái)做一個(gè)簡(jiǎn)單的 Electron 應(yīng)用,要求為應(yīng)用啟動(dòng)后顯示一個(gè)主窗口,在主窗口里有一個(gè)按鈕,點(diǎn)擊這個(gè)按鈕后新顯示一個(gè)窗口,且使用 React 開發(fā)網(wǎng)頁(yè)。
由于 Electron 應(yīng)用中的每一個(gè)窗口對(duì)應(yīng)一個(gè)網(wǎng)頁(yè),所以需要開發(fā)2個(gè)網(wǎng)頁(yè),分別是主窗口的 index.html 和新打開的窗口 login.html。
需要改動(dòng)的地方如下:
在項(xiàng)目根目錄下新建主進(jìn)程的入口文件 main.js,內(nèi)容和上面提到的一致;
主窗口網(wǎng)頁(yè)的代碼如下:
import React, { Component } from "react"; import { render } from "react-dom"; import { remote } from "electron"; import path from "path"; import "./index.css"; class App extends Component { // 在按鈕被點(diǎn)擊時(shí) handleBtnClick() { // 新窗口對(duì)應(yīng)的頁(yè)面的 URI 地址 const modalPath = path.join("file://", remote.app.getAppPath(), "dist/login.html"); // 新窗口的大小 let win = new remote.BrowserWindow({ width: 400, height: 320 }) win.on("close", function () { // 窗口被關(guān)閉時(shí)清空資源 win = null }) // 加載網(wǎng)頁(yè) win.loadURL(modalPath) // 顯示窗口 win.show() } render() { return () } } render(Page Index
, window.document.getElementById("app"));
其中最關(guān)鍵的部分在于在按鈕點(diǎn)擊事件里通過(guò) electron 庫(kù)里提供的 API 去新打開一個(gè)窗口,并加載網(wǎng)頁(yè)文件所在的地址。
頁(yè)面部分的代碼已經(jīng)修改完成,接下來(lái)修改構(gòu)建方面的代碼。 這里構(gòu)建需要做到以下幾點(diǎn):
構(gòu)建出2個(gè)可在瀏覽器里運(yùn)行的網(wǎng)頁(yè),分別對(duì)應(yīng)2個(gè)窗口的界面;
由于在網(wǎng)頁(yè)的 JavaScript 代碼里可能會(huì)有調(diào)用 Node.js 原生模塊或者 electron 模塊,也就是輸出的代碼依賴這些模塊。但由于這些模塊都是內(nèi)置支持的,構(gòu)建出的代碼不能把這些模塊打包進(jìn)去。
要完成以上要求非常簡(jiǎn)單,因?yàn)?Webpack 內(nèi)置了對(duì) Electron 的支持。 只需要給 Webpack 配置文件加上一行代碼即可,如下:
target: "electron-renderer",
以上修改都完成后重新執(zhí)行 Webpack 構(gòu)建,對(duì)應(yīng)的網(wǎng)頁(yè)需要的代碼都輸出到了項(xiàng)目根目錄下的 dist 目錄里。
為了以 Electron 應(yīng)用的形式運(yùn)行,還需要安裝新依賴:
# 安裝 Electron 執(zhí)行環(huán)境到項(xiàng)目中 npm i -D electron構(gòu)建Npm模塊
發(fā)布到 Npm 倉(cāng)庫(kù)的模塊有以下幾個(gè)特點(diǎn):
每個(gè)模塊根目錄下都必須有一個(gè)描述該模塊的 package.json 文件。該文件描述了模塊的入口文件是哪個(gè),該模塊又依賴哪些模塊等。
模塊中的文件以 JavaScript 文件為主,但不限于 JavaScript 文件。例如一個(gè) UI 組件模塊可能同時(shí)需要 JavaScript、CSS、圖片文件等。
模塊中的代碼大多采用模塊化規(guī)范,因?yàn)槟愕倪@個(gè)模塊可能依賴其它模塊,而且別的模塊又可能依賴你的這個(gè)模塊。因?yàn)槟壳爸С直容^廣泛的是 CommonJS 模塊化規(guī)范,上傳到 Npm 倉(cāng)庫(kù)的代碼最好遵守該規(guī)范。
拋出問(wèn)題Webpack 不僅可用于構(gòu)建運(yùn)行的應(yīng)用,也可用于構(gòu)建上傳到 Npm 的模塊。 接下來(lái)用教大家如何用 Webpack 構(gòu)建一個(gè)可上傳的 Npm 倉(cāng)庫(kù)的 React 組件,具體要求如下:
源代碼采用 ES6 寫,但發(fā)布到 Npm 倉(cāng)庫(kù)的需要是 ES5 的,并且遵守 CommonJS 模塊化規(guī)范。如果發(fā)布到 Npm 上去的 ES5 代碼是經(jīng)過(guò)轉(zhuǎn)換的,請(qǐng)同時(shí)提供 Source Map 以方便調(diào)試。
該 UI 組件依賴的其它資源文件例如 CSS 文件也需要包含在發(fā)布的模塊里。
盡量減少冗余代碼,減少發(fā)布出去的組件的代碼文件大小。
發(fā)布出去的組件的代碼中不能含有其依賴的模塊的代碼,而是讓用戶可選擇性的去安裝。例如不能內(nèi)嵌 React 庫(kù)的代碼,這樣做的目的是在其它組件也依賴 React 庫(kù)時(shí),防止 React 庫(kù)的代碼被重復(fù)打包。
在開始前先看下最終發(fā)布到 Npm 倉(cāng)庫(kù)的模塊的目錄結(jié)構(gòu):
node_modules/hello-webpack ├── lib │ ├── index.css (組件所有依賴的 CSS 都在這個(gè)文件中) │ ├── index.css.map │ ├── index.js (符合 CommonJS 模塊化規(guī)范的 ES5 代碼) │ └── index.js.map ├── src (ES6 源碼) │ ├── index.css │ └── index.js └── package.json (模塊描述文件)
src/index.js 文件,內(nèi)容如下:
import React, { Component } from "react"; import "./index.css"; // 導(dǎo)出該組件供給其它模塊使用 export default class HelloWebpack extends Component { render() { returnHello,Webpack
} }
要使用該模塊時(shí)只需要這樣:
// 通過(guò) ES6 語(yǔ)法導(dǎo)入 import HelloWebpack from "hello-webpack"; import "hello-webpack/lib/index.css"; // 或者通過(guò) ES5 語(yǔ)法導(dǎo)入 var HelloWebpack = require("hello-webpack"); require("hello-webpack/lib/index.css"); // 使用 react-dom 渲染 render(使用 Webpack 構(gòu)建 Npm 模塊);
對(duì)于要求1,可以這樣做到:
使用 babel-loader 把 ES6 代碼轉(zhuǎn)換成 ES5 的代碼。
通過(guò)開啟 devtool: "source-map" 輸出 Source Map 以發(fā)布調(diào)試。
設(shè)置 output.libraryTarget="commonjs2" 使輸出的代碼符合CommonJS2 模塊化規(guī)范,以供給其它模塊導(dǎo)入使用。
相關(guān)的 Webpack 配置代碼如下:
module.exports = { output: { // 輸出的代碼符合 CommonJS 模塊化規(guī)范,以供給其它模塊導(dǎo)入使用。 libraryTarget: "commonjs2", }, // 輸出 Source Map devtool: "source-map", };
對(duì)于要求2,需要通過(guò) css-loader 和 extract-text-webpack-plugin 實(shí)現(xiàn),相關(guān)的 Webpack 配置代碼如下:
const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { module: { rules: [ { // 增加對(duì) CSS 文件的支持 test: /.css/, // 提取出 Chunk 中的 CSS 代碼到多帶帶的文件中 use: ExtractTextPlugin.extract({ use: ["css-loader"] }), }, ] }, plugins: [ new ExtractTextPlugin({ // 輸出的 CSS 文件名稱 filename: "index.css", }), ], };
此步引入了3個(gè)新依賴:
# 安裝 Webpack 構(gòu)建所需要的新依賴 npm i -D style-loader css-loader extract-text-webpack-plugin
對(duì)于要求3,需要注意的是 Babel 在把 ES6 代碼轉(zhuǎn)換成 ES5 代碼時(shí)會(huì)注入一些輔助函數(shù)。
例如下面這段 ES6 代碼:
class HelloWebpack extends Component{ }
在被轉(zhuǎn)換成能正常運(yùn)行的 ES5 代碼時(shí)需要以下2個(gè)輔助函數(shù):
babel-runtime/helpers/createClass 用于實(shí)現(xiàn) class 語(yǔ)法
babel-runtime/helpers/inherits 用于實(shí)現(xiàn) extends 語(yǔ)法
默認(rèn)的情況下 Babel 會(huì)在每個(gè)輸出文件中內(nèi)嵌這些依賴的輔助函數(shù)的代碼,如果多個(gè)源代碼文件都依賴這些輔助函數(shù),那么這些輔助函數(shù)的代碼將會(huì)重復(fù)的出現(xiàn)很多次,造成代碼冗余。
為了不讓這些輔助函數(shù)的代重復(fù)出現(xiàn),可以在依賴它們的時(shí)候通過(guò) require("babel-runtime/helpers/createClass") 的方式去導(dǎo)入,這樣就能做到只讓它們出現(xiàn)一次。 babel-plugin-transform-runtime 插件就是用來(lái)做這個(gè)事情的。
修改 .babelrc 文件,為其加入 transform-runtime 插件:
{ "plugins": [ [ "transform-runtime", { // transform-runtime 默認(rèn)會(huì)自動(dòng)的為你使用的 ES6 API 注入 polyfill // 假如你在源碼中使用了 Promise,輸出的代碼將會(huì)自動(dòng)注入 require("babel-runtime/core-js/Promise") 語(yǔ)句 // polyfill 的注入應(yīng)該交給模塊使用者,因?yàn)槭褂谜呖赡茉谄渌胤揭呀?jīng)注入了其它的 Promise polyfill 庫(kù) // 所以關(guān)閉該功能 "polyfill": false } ] ] }
由于加入 babel-plugin-transform-runtime 后生成的代碼中會(huì)大量出現(xiàn)類似 require("babel-runtime/helpers/createClass") 這樣的語(yǔ)句,所以輸出的代碼將依賴 babel-runtime 模塊。
此步引入了3個(gè)新依賴:
# 安裝 Webpack 構(gòu)建所需要的新依賴 npm i -D babel-plugin-transform-runtime # 安裝輸出代碼運(yùn)行時(shí)所需的新依賴 npm i -S babel-runtime
對(duì)于要求4,需要通過(guò)在 其它配置項(xiàng) 中介紹過(guò)的 Externals 來(lái)實(shí)現(xiàn)。
Externals 用來(lái)告訴 Webpack 要構(gòu)建的代碼中使用了哪些不用被打包的模塊,也就是說(shuō)這些模版是外部環(huán)境提供的,Webpack 在打包時(shí)可以忽略它們。
相關(guān)的 Webpack 配置代碼如下:
module.exports = { // 通過(guò)正則命中所有以 react 或者 babel-runtime 開頭的模塊 // 這些模塊通過(guò)注冊(cè)在運(yùn)行環(huán)境中的全局變量訪問(wèn),不用被重復(fù)打包進(jìn)輸出的代碼里 externals: /^(react|babel-runtime)/, };
開啟以上配置后,輸出的代碼中會(huì)存在導(dǎo)入 react 或者 babel-runtime 模塊的代碼,但是它們的 react 或者 babel-runtime 的內(nèi)容不會(huì)被包含進(jìn)去,如下:
[ (function (module, exports) { module.exports = require("babel-runtime/helpers/inherits"); }), (function (module, exports) { module.exports = require("react"); }) ]
這樣就做到了在保持代碼正確性的情況下,輸出文件不存放 react 或者 babel-runtime 模塊的代碼。
實(shí)際上當(dāng)你在開發(fā) Npm 模塊時(shí),不只需要對(duì) react 和 babel-runtime 模塊做這樣的處理,而是需要對(duì)所有正在開發(fā)的模塊所依賴的模塊進(jìn)行這樣的處理。 因?yàn)檎陂_發(fā)的模塊所依賴的模塊也可能被其它模塊所依賴。 當(dāng)一個(gè)項(xiàng)目中一個(gè)模塊被依賴多次時(shí),Webpack 只會(huì)將其打包一次。
完成以上4步后最終的 Webpack 完整配置代碼如下:
const path = require("path"); const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { // 模塊的入口文件 entry: "./src/index.js", output: { // 輸出文件的名稱 filename: "index.js", // 輸出文件的存放目錄 path: path.resolve(__dirname, "lib"), // 輸出的代碼符合 CommonJS 模塊化規(guī)范,以供給其它模塊導(dǎo)入使用。 libraryTarget: "commonjs2", }, // 通過(guò)正則命中所有以 react 或者 babel-runtime 開頭的模塊, // 這些模塊使用外部的,不能被打包進(jìn)輸出的代碼里,防止它們出現(xiàn)多次。 externals: /^(react|babel-runtime)/, module: { rules: [ { test: /.js$/, use: ["babel-loader"], // 排除 node_modules 目錄下的文件, // node_modules 目錄下的文件都是采用的 ES5 語(yǔ)法,沒(méi)必要再通過(guò) Babel 去轉(zhuǎn)換。 exclude: path.resolve(__dirname, "node_modules"), }, { // 增加對(duì) CSS 文件的支持 test: /.css/, // 提取出 Chunk 中的 CSS 代碼到多帶帶的文件中 use: ExtractTextPlugin.extract({ use: ["css-loader"] }), }, ] }, plugins: [ new ExtractTextPlugin({ // 輸出的 CSS 文件名稱 filename: "index.css", }), ], // 輸出 Source Map devtool: "source-map", };
重新執(zhí)行構(gòu)建后,你將會(huì)在項(xiàng)目目錄下看到一個(gè)新目錄 lib,里面放著要發(fā)布到 Npm 倉(cāng)庫(kù)的最終代碼。
發(fā)布到 Npm在把構(gòu)建出的代碼發(fā)布到 Npm 倉(cāng)庫(kù)前,還需要確保你的模塊描述文件 package.json 是正確配置的。
由于構(gòu)建出的代碼的入口文件是 ./lib/index.js,需要修改 package.json 中的 main 字段如下:
{ "main": "lib/index.js", "jsnext:main": "src/index.js" }
其中 jsnext:main 字段用于指出采用 ES6 編寫的模塊入口文件所在的位置。
修改完畢后在項(xiàng)目目錄下執(zhí)行 npm publish 就能把構(gòu)建出的代碼發(fā)布到 Npm 倉(cāng)庫(kù)中(確保已經(jīng) npm login 過(guò))。
如果你想讓發(fā)布到 Npm 上去的代碼保持和源碼的目錄結(jié)構(gòu)一致,那么用 Webpack 將不在適合。 因?yàn)樵创a是一個(gè)個(gè)分割的模塊化文件,而 Webpack 會(huì)把這些模塊組合在一起。 雖然 Webpack 輸出的文件也可以是采用 CommonJS 模塊化語(yǔ)法的,但在有些場(chǎng)景下把所有模塊打包成一個(gè)文件發(fā)布到 Npm 是不適合的。 例如像 Lodash 這樣的工具函數(shù)庫(kù)在項(xiàng)目中可能只用到了其中幾個(gè)工具函數(shù),如果所有工具函數(shù)打包在一個(gè)文件中,那么所有工具函數(shù)都會(huì)被打包進(jìn)去,而保持模塊文件的獨(dú)立能做到只打包進(jìn)使用到的。 還有就是像 UI 組件庫(kù)這樣由大量獨(dú)立組件組成的庫(kù)也和 Lodash 類似。構(gòu)建離線應(yīng)用
所以 Webpack 適合于構(gòu)建完整不可分割的 Npm 模塊。
離線應(yīng)用的核心是離線緩存技術(shù),歷史上曾先后出現(xiàn)2種離線離線緩存技術(shù),它們分別是:
AppCache 又叫 Application Cache,目前已經(jīng)從 Web 標(biāo)準(zhǔn)中刪除,請(qǐng)盡量不要使用它。
Service Workers 是目前最新的離線緩存技術(shù),是 Web Worker 的一部分。 它通過(guò)攔截網(wǎng)絡(luò)請(qǐng)求實(shí)現(xiàn)離線緩存,比 AppCache 更加靈活。它也是構(gòu)建 PWA 應(yīng)用的關(guān)鍵技術(shù)之一。
認(rèn)識(shí) Service WorkersService Workers 是一個(gè)在瀏覽器后臺(tái)運(yùn)行的腳本,它生命周期完全獨(dú)立于網(wǎng)頁(yè)。它無(wú)法直接訪問(wèn) DOM,但可以通過(guò) postMessage 接口發(fā)送消息來(lái)和 UI 進(jìn)程通信。 攔截網(wǎng)絡(luò)請(qǐng)求是 Service Workers 的一個(gè)重要功能,通過(guò)它能完成離線緩存、編輯響應(yīng)、過(guò)濾響應(yīng)等功能。
Service Workers 兼容性目前 Chrome、Firefox、Opera 都已經(jīng)全面支持 Service Workers,但對(duì)于移動(dòng)端瀏覽器就不太樂(lè)觀了,只有高版本的 Android 支持。 由于 Service Workers 無(wú)法通過(guò)注入 polyfill 去實(shí)現(xiàn)兼容,所以在你打算使用它前請(qǐng)先調(diào)查清楚你的網(wǎng)頁(yè)的運(yùn)行場(chǎng)景。
判斷瀏覽器是否支持 Service Workers 的最簡(jiǎn)單的方法是通過(guò)以下代碼:
// 如果 navigator 對(duì)象上存在 serviceWorker 對(duì)象,就表示支持 if (navigator.serviceWorker) { // 通過(guò) navigator.serviceWorker 使用 }注冊(cè) Service Workers
要給網(wǎng)頁(yè)接入 Service Workers,需要在網(wǎng)頁(yè)加載后注冊(cè)一個(gè)描述 Service Workers 邏輯的腳本。 代碼如下:
if (navigator.serviceWorker) { window.addEventListener("DOMContentLoaded",function() { // 調(diào)用 serviceWorker.register 注冊(cè),參數(shù) /sw.js 為腳本文件所在的 URL 路徑 navigator.serviceWorker.register("/sw.js"); }); }
一旦這個(gè)腳本文件被加載,Service Workers 的安裝就開始了。這個(gè)腳本被安裝到瀏覽器中后,就算用戶關(guān)閉了當(dāng)前網(wǎng)頁(yè),它仍會(huì)存在。 也就是說(shuō)第一次打開該網(wǎng)頁(yè)時(shí) Service Workers 的邏輯不會(huì)生效,因?yàn)槟_本還沒(méi)有被加載和注冊(cè),但是以后再次打開該網(wǎng)頁(yè)時(shí)腳本里的邏輯將會(huì)生效。
在 Chrome 中可以通過(guò)打開網(wǎng)址 chrome://inspect/#service-workers 來(lái)查看當(dāng)前瀏覽器中所有注冊(cè)了的 Service Workers。
使用 Service Workers 實(shí)現(xiàn)離線緩存Service Workers 在注冊(cè)成功后會(huì)在其生命周期中派發(fā)出一些事件,通過(guò)監(jiān)聽對(duì)應(yīng)的事件在特點(diǎn)的時(shí)間節(jié)點(diǎn)上做一些事情。
在 Service Workers 腳本中,引入了新的關(guān)鍵字 self 代表當(dāng)前的 Service Workers 實(shí)例。
在 Service Workers 安裝成功后會(huì)派發(fā)出 install 事件,需要在這個(gè)事件中執(zhí)行緩存資源的邏輯,實(shí)現(xiàn)代碼如下:
// 當(dāng)前緩存版本的唯一標(biāo)識(shí)符,用當(dāng)前時(shí)間代替 var cacheKey = new Date().toISOString(); // 需要被緩存的文件的 URL 列表 var cacheFileList = [ "/index.html", "/app.js", "/app.css" ]; // 監(jiān)聽 install 事件 self.addEventListener("install", function (event) { // 等待所有資源緩存完成時(shí),才可以進(jìn)行下一步 event.waitUntil( caches.open(cacheKey).then(function (cache) { // 要緩存的文件 URL 列表 return cache.addAll(cacheFileList); }) ); });
接下來(lái)需要監(jiān)聽網(wǎng)絡(luò)請(qǐng)求事件去攔截請(qǐng)求,復(fù)用緩存,代碼如下:
self.addEventListener("fetch", function(event) { event.respondWith( // 去緩存中查詢對(duì)應(yīng)的請(qǐng)求 caches.match(event.request).then(function(response) { // 如果命中本地緩存,就直接返回本地的資源 if (response) { return response; } // 否則就去用 fetch 下載資源 return fetch(event.request); } ) ); });
以上就實(shí)現(xiàn)了離線緩存。
更新緩存線上的代碼有時(shí)需要更新和重新發(fā)布,如果這個(gè)文件被離線緩存了,那就需要 Service Workers 腳本中有對(duì)應(yīng)的邏輯去更新緩存。 這可以通過(guò)更新 Service Workers 腳本文件做到。
瀏覽器針對(duì) Service Workers 有如下機(jī)制:
每次打開接入了 Service Workers 的網(wǎng)頁(yè)時(shí),瀏覽器都會(huì)去重新下載 Service Workers 腳本文件(所以要注意該腳本文件不能太大),如果發(fā)現(xiàn)和當(dāng)前已經(jīng)注冊(cè)過(guò)的文件存在字節(jié)差異,就將其視為“新服務(wù)工作線程”。
新 Service Workers 線程將會(huì)啟動(dòng),且將會(huì)觸發(fā)其 install 事件。
當(dāng)網(wǎng)站上當(dāng)前打開的頁(yè)面關(guān)閉時(shí),舊 Service Workers 線程將會(huì)被終止,新 Service Workers 線程將會(huì)取得控制權(quán)。
新 Service Workers 線程取得控制權(quán)后,將會(huì)觸發(fā)其 activate 事件。
新 Service Workers 線程中的 activate 事件就是最佳的清理舊緩存的時(shí)間點(diǎn),
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/95258.html
摘要:官方文檔中文翻譯構(gòu)建用戶界面的庫(kù)。官方文檔建議學(xué)習(xí)時(shí)以官方文檔為準(zhǔn),中文翻譯或者第三方作者的教程可以幫助你理清思路會(huì)用到的重要知識(shí)點(diǎn)我也會(huì)進(jìn)行簡(jiǎn)明的解釋,如遇到錯(cuò)誤或者不理解的內(nèi)容,歡迎實(shí)時(shí)指出。 前言 前面提到前端大統(tǒng)一的概念,如果感興趣,歡迎說(shuō)說(shuō)自己的看法,點(diǎn)擊前往。Web前端框架層出不窮,不可能面面俱到,這里給個(gè)小建議: 如果對(duì)Weex App感興趣,應(yīng)該選擇vue框架; 如果...
摘要:官方文檔中文翻譯構(gòu)建用戶界面的庫(kù)。官方文檔建議學(xué)習(xí)時(shí)以官方文檔為準(zhǔn),中文翻譯或者第三方作者的教程可以幫助你理清思路會(huì)用到的重要知識(shí)點(diǎn)我也會(huì)進(jìn)行簡(jiǎn)明的解釋,如遇到錯(cuò)誤或者不理解的內(nèi)容,歡迎實(shí)時(shí)指出。 前言 前面提到前端大統(tǒng)一的概念,如果感興趣,歡迎說(shuō)說(shuō)自己的看法,點(diǎn)擊前往。Web前端框架層出不窮,不可能面面俱到,這里給個(gè)小建議: 如果對(duì)Weex App感興趣,應(yīng)該選擇vue框架; 如果...
摘要:中添加同樣起作用。說(shuō)明提供的命令,打包時(shí)模塊綁定的加載器為命令,監(jiān)聽打包的文件,只要改變自動(dòng)會(huì)打包命令窗口顯示打包進(jìn)度命令窗口列出引入的所有模塊命令窗口顯示打包引入模塊的原因 標(biāo)注:本筆記來(lái)自 imooc-qbaty老師-webpack深入與實(shí)戰(zhàn)gitbash(or CMD)進(jìn)行命令操作 一、準(zhǔn)備工作 lenovo@lenovo-PC MINGW64 ~ $ cd /d/imooc/ ...
摘要:而且作為一個(gè)版本,其中有一些,可能會(huì)導(dǎo)致一些配置和插件不工作。但這并無(wú)妨礙我們?nèi)ラ_始對(duì)上的新特性進(jìn)行嘗鮮實(shí)戰(zhàn)。 作者:志佳老師 本文首發(fā)微信公眾號(hào):jingchengyideng歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 前言 在老袁寫這篇文章的時(shí)候,v5版本仍然處于早期階段,可能仍然有問(wèn)題。而且作為一個(gè)major版本,其中有一些breaking changes,可能會(huì)導(dǎo)致一些配置...
摘要:好了,閑話不多說(shuō)今天要說(shuō)的時(shí)利用監(jiān)聽路由的方式,實(shí)現(xiàn)同個(gè)頁(yè)面不同狀態(tài)的切換。只要等于,那么頁(yè)面就是待確認(rèn)回款頁(yè)面進(jìn)入待確認(rèn)回款頁(yè)面中,回款狀態(tài)的篩選標(biāo)簽要加上。 1.前言 今天發(fā)完這一篇,就要這個(gè)系列告一段落了!以后如果有什么要補(bǔ)充的會(huì)繼續(xù)補(bǔ)充!因?yàn)樵诤笈_(tái)管理項(xiàng)目上,搭建的話,主要就是這樣了!還有的一些是具體到交互的處理,那個(gè)是要根據(jù)后端的需求,來(lái)進(jìn)來(lái)比較細(xì)化的工作,我在這里就不說(shuō)了!...
摘要:本文相關(guān)代碼已經(jīng)存放在,可自行下載使用多入口復(fù)習(xí)的優(yōu)勢(shì)不言而喻,因此在實(shí)際應(yīng)用中我們也常常使用它調(diào)試多入口應(yīng)用,所謂多入口是指多個(gè)頁(yè)面會(huì)使用多個(gè)入口文件,在官方教程介紹了如何配置這里指定了個(gè)入口文件,打包之后分別會(huì)在文件夾中生成個(gè)打包之后 本文相關(guān)代碼已經(jīng)存放在 dynamic-entry,可自行下載使用 0. 多入口 (復(fù)習(xí)) webpack 的優(yōu)勢(shì)不言而喻,因此在實(shí)際應(yīng)用中我們也常...
閱讀 3518·2021-11-22 12:00
閱讀 751·2019-08-29 13:24
閱讀 2967·2019-08-29 11:31
閱讀 2681·2019-08-26 14:00
閱讀 3270·2019-08-26 11:42
閱讀 2538·2019-08-23 18:31
閱讀 867·2019-08-23 18:27
閱讀 2908·2019-08-23 16:58